General Setup


Create a new analysis directory...
[1] FALSE
[1] FALSE
[1] FALSE
[1] FALSE
[1] FALSE
[1] FALSE
[1] "/Users/slaan3/git/CirculatoryHealth/AE_20211201_YAW_SWVANDERLAAN_HDAC9"
 [1] "_archived"                                          "1. AE_20211201_YAW_SWVANDERLAAN_HDAC9.nb.html"     
 [3] "1. AE_20211201_YAW_SWVANDERLAAN_HDAC9.Rmd"          "1. AEDB.CEA.baseline.nb.html"                      
 [5] "1. AEDB.CEA.baseline.Rmd"                           "2. SNP_analyses.nb.html"                           
 [7] "2. SNP_analyses.Rmd"                                "20220319.HDAC9.AEDB.CEA.baseline.RData"            
 [9] "20220319.HDAC9.AESCRNA.results.RData"               "20220319.HDAC9.bulkRNAseq.additional_figures.RData"
[11] "20220319.HDAC9.bulkRNAseq.main_analysis.RData"      "20220319.HDAC9.bulkRNAseq.preparation.RData"       
[13] "20230301.HDAC9.bulkRNAseq.additional_figures.RData" "20230511.HDAC9.bulkRNAseq.additional_figures.RData"
[15] "20230531.HDAC9.bulkRNAseq.additional_figures.RData" "3.1 bulkRNAseq.preparation.nb.html"                
[17] "3.1 bulkRNAseq.preparation.Rmd"                     "3.2 bulkRNAseq.main_analysis.nb.html"              
[19] "3.2 bulkRNAseq.main_analysis.Rmd"                   "3.3 bulkRNAseq.additional_figures.nb.html"         
[21] "3.3 bulkRNAseq.additional_figures.Rmd"              "4. scRNAseq.nb.html"                               
[23] "4. scRNAseq.Rmd"                                    "AE_20211201_YAW_SWVANDERLAAN_HDAC9.Rproj"          
[25] "AnalysisPlan"                                       "HDAC9"                                             
[27] "images"                                             "LICENSE"                                           
[29] "README.html"                                        "README.md"                                         
[31] "references.bib"                                     "renv"                                              
[33] "renv.lock"                                          "scripts"                                           
[35] "SNP"                                               
source(paste0(PROJECT_loc, "/scripts/functions.R"))
install.packages.auto("readr")
install.packages.auto("optparse")
install.packages.auto("tools")
install.packages.auto("dplyr")
install.packages.auto("tidyr")
install.packages.auto("naniar")

# To get 'data.table' with 'fwrite' to be able to directly write gzipped-files
# Ref: https://stackoverflow.com/questions/42788401/is-possible-to-use-fwrite-from-data-table-with-gzfile
# install.packages("data.table", repos = "https://Rdatatable.gitlab.io/data.table")
library(data.table)

install.packages.auto("tidyverse")
install.packages.auto("knitr")
install.packages.auto("DT")
install.packages.auto("MASS")
# install.packages.auto("Seurat") # latest version

# Install the devtools package from Hadley Wickham
install.packages.auto('devtools')

install.packages.auto("haven")
install.packages.auto("sjlabelled")
install.packages.auto("sjPlot")
install.packages.auto("labelled")
install.packages.auto("tableone")

install.packages.auto("ggpubr")

Background

This notebook contains additional figures of the project “Plaque expression levels of HDAC9 in association with plaque vulnerability traits and secondary vascular events in patients undergoing carotid endarterectomy: an analysis in the Athero-EXPRESS Biobank.”.

Loading data

# load(paste0(PROJECT_loc, "/",Today,".",PROJECTNAME,".bulkRNAseq.main_analysis.RData"))
load(paste0(PROJECT_loc, "/20220319.",PROJECTNAME,".bulkRNAseq.main_analysis.RData"))

Fix some variables

We need to get the ‘conventional unit’ versions of cholesterols.

AERNASE.clin.hdac9 <- merge(AERNASE.clin.hdac9, 
                            subset(AEDB.CEA, select = c("STUDY_NUMBER", 
                                                        "risk614", 
                                                        "LDL_finalCU", "HDL_finalCU", "TC_finalCU", "TG_finalCU")), 
                            by.x = "STUDY_NUMBER", by.y = "STUDY_NUMBER", sort = TRUE, all.x = TRUE)

Additional figures

Age and sex

We want to create per-age-group figures median ± interquartile range.

  • Box and Whisker plot for target(s) plaque levels by sex.
  • Box and Whisker plot for target(s) plaque levels by (sex and) age group (<55, 55-64, 65-74, 75-84, 85+).

# ?ggpubr::ggboxplot()
compare_means(HDAC9 ~ Gender,  data = AERNASE.clin.hdac9, method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9, 
                  x = c("Gender"),
                  y = "HDAC9", 
                  xlab = "gender",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Gender.pdf"), plot = last_plot())
Saving 12 x 8 in image
library(dplyr)

AERNASE.clin.hdac9 <- AERNASE.clin.hdac9 %>% dplyr::mutate(AgeGroup = factor(case_when(Age < 55 ~ "<55",
                                                     Age >= 55  & Age <= 64 ~ "55-64",
                                                     Age >= 65  & Age <= 74 ~ "65-74",
                                                     Age >= 75  & Age <= 84 ~ "75-84",
                                                     Age >= 85 ~ "85+"))) 

AERNASE.clin.hdac9 <- AERNASE.clin.hdac9 %>% dplyr::mutate(AgeGroupSex = factor(case_when(Age < 55 & Gender == "male" ~ "<55 males" ,
                                                        Age >= 55  & Age <= 64 & Gender == "male"~ "55-64 males",
                                                        Age >= 65  & Age <= 74 & Gender == "male"~ "65-74 males",
                                                        Age >= 75  & Age <= 84 & Gender == "male"~ "75-84 males",
                                                        Age >= 85 & Gender == "male"~ "85+ males",
                                                        Age < 55 & Gender == "female" ~ "<55 females" ,
                                                        Age >= 55  & Age <= 64 & Gender == "female"~ "55-64 females ",
                                                        Age >= 65  & Age <= 74 & Gender == "female"~ "65-74 females",
                                                        Age >= 75  & Age <= 84 & Gender == "female"~ "75-84 females",
                                                        Age >= 85 & Gender == "female"~ "85+ females")))

table(AERNASE.clin.hdac9$AgeGroup, AERNASE.clin.hdac9$Gender)
       
        female male
  <55       11   27
  55-64     43  124
  65-74     58  191
  75-84     37  119
  85+        4    9
table(AERNASE.clin.hdac9$AgeGroupSex)

   <55 females      <55 males 55-64 females     55-64 males  65-74 females    65-74 males  75-84 females    75-84 males    85+ females 
            11             27             43            124             58            191             37            119              4 
     85+ males 
             9 

Now we can draw some graphs of plaque target(s) levels per sex and age group as median ± interquartile range.


# ?ggpubr::ggboxplot()
compare_means(HDAC9 ~ AgeGroup,  data = AERNASE.clin.hdac9, method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9, 
                  x = c("AgeGroup"),
                  y = "HDAC9", 
                  xlab = "Age groups (years)",
                  ylab = "HDAC9 (normalized expression)",
                  color = "AgeGroup",
                  palette = "npg",
                  # add = "median_iqr")
                  add = c("median_iqr", "jitter")) +
  stat_compare_means(aes(group = AgeGroup), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.AgeGroup.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ AgeGroup, group.by = "Gender", data = AERNASE.clin.hdac9, method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9, 
                  x = c("AgeGroup"),
                  y = "HDAC9", 
                  xlab = "Age groups (years) per gender",
                  ylab = "HDAC9 (normalized expression",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  # add = "median_iqr")
                  add = c("median_iqr", "jitter")) +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.AgeGroup_perGender.pdf"), plot = last_plot())
Saving 12 x 8 in image

Hypertension & blood pressure

We want to create figures of target(s) levels stratified by hypertension/blood pressure, and use of anti-hypertensive drugs.

  • Box and Whisker plot for target(s) plaque levels by hypertension group (no, yes)
  • Box and Whisker plot for target(s) plaque levels by systolic blood pressure group (<120, 120-139, 140-159,160+)
library(dplyr)

AERNASE.clin.hdac9 <- AERNASE.clin.hdac9 %>% mutate(SBPGroup = factor(case_when(systolic < 120 ~ "<120",
                                                     systolic >= 120  & systolic <= 139 ~ "120-139",
                                                     systolic >= 140  & systolic <= 159 ~ "140-159",
                                                     systolic >= 160 ~ "160+"))) 

table(AERNASE.clin.hdac9$SBPGroup, AERNASE.clin.hdac9$Gender)
         
          female male
  <120         7   22
  120-139     30   81
  140-159     36  120
  160+        62  169

Now we can draw some graphs of plaque target(s) levels per sex and hypertension/blood pressure group as median ± interquartile range.

compare_means(HDAC9 ~ SBPGroup, data = AERNASE.clin.hdac9 %>% filter(!is.na(SBPGroup)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(SBPGroup)), 
                  x = c("SBPGroup"),
                  y = "HDAC9", 
                  xlab = "Systolic blood pressure (mmHg)",
                  ylab = "HDAC9 (normalized expression)",
                  color = "SBPGroup",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(aes(group = SBPGroup), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.SBPGroup.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ Hypertension.selfreport, data = AERNASE.clin.hdac9 %>% filter(!is.na(Hypertension.selfreport)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Hypertension.selfreport)), 
                  x = c("Hypertension.selfreport"),
                  y = "HDAC9", 
                  xlab = "Self-reported hypertension",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Hypertension.selfreport",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(aes(group = Hypertension.selfreport), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Hypertension.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ Hypertension.drugs, data = AERNASE.clin.hdac9 %>% filter(!is.na(Hypertension.drugs)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Hypertension.drugs)), 
                  x = c("Hypertension.drugs"),
                  y = "HDAC9", 
                  xlab = "Hypertension medication use",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Hypertension.drugs",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(aes(group = Hypertension.drugs), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.HypertensionDrugs.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ SBPGroup, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(SBPGroup)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(SBPGroup)), 
                  x = c("SBPGroup"),
                  y = "HDAC9", 
                  xlab = "Systolic blood pressure (mmHg) per gender",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.SBPGroup_byGender.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ Hypertension.selfreport, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(Hypertension.selfreport)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Hypertension.selfreport)), 
                  x = c("Hypertension.selfreport"),
                  y = "HDAC9", 
                  xlab = "Self-reported hypertension per gender",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Hypertension_byGender.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ Hypertension.drugs, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(Hypertension.drugs)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Hypertension.drugs)), 
                  x = c("Hypertension.drugs"),
                  y = "HDAC9", 
                  xlab = "Hypertension medication use per gender",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Hypertension.drugs_byGender.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ SBPGroup, group.by = "Hypertension.drugs", data = AERNASE.clin.hdac9 %>% filter(!is.na(SBPGroup) & !is.na(Hypertension.drugs)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(SBPGroup) & !is.na(Hypertension.drugs)), 
                  x = c("SBPGroup"),
                  y = "HDAC9", 
                  xlab = "Systolic blood pressure (mmHg) by medication use",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Hypertension.drugs",
                  palette = c("#49A01D", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Hypertension.drugs), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.SBPGroup_byHypertensionDrugs.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ Hypertension.selfreport, group.by = "Hypertension.drugs", data = AERNASE.clin.hdac9 %>% filter(!is.na(Hypertension.selfreport) & !is.na(Hypertension.drugs)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Hypertension.selfreport) & !is.na(Hypertension.drugs)), 
                  x = c("Hypertension.selfreport"),
                  y = "HDAC9", 
                  xlab = "Self-reported hypertension per medication use",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Hypertension.drugs",
                  palette = c("#49A01D", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Hypertension.drugs), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Hypertension.selfreport_byHypertensionDrugs.pdf"), plot = last_plot())
Saving 12 x 8 in image

Hypercholesterolemia & LDL levels

We want to create figures of target(s) levels stratified by hypercholesterolemia/LDL-levels, and use of lipid-lowering drugs.

  • Box and Whisker plot for target(s) plaque levels by hypercholesterolemia (risk614) group (no, yes)
  • Box and Whisker plot for target(s) plaque levels by lipid-lowering drugs group (no, yes)
  • Box and Whisker plot for target(s) plaque levels by LDL-levels (mmol/L) group (<100, 100-129, 130-159, 160-189, 190+)
library(dplyr)

AERNASE.clin.hdac9 <- AERNASE.clin.hdac9 %>% mutate(LDLGroup = factor(case_when(LDL_finalCU < 100 ~ "<100",
                                                     LDL_finalCU >= 100  & LDL_finalCU <= 129 ~ "100-129",
                                                     LDL_finalCU >= 130  & LDL_finalCU <= 159 ~ "130-159",
                                                     LDL_finalCU >= 160  & LDL_finalCU <= 189 ~ "160-189",
                                                     LDL_finalCU >= 190 ~ "190+"))) 


table(AERNASE.clin.hdac9$LDLGroup, AERNASE.clin.hdac9$Gender)
         
          female male
  <100        45  142
  100-129     25   73
  130-159     18   42
  160-189      9   21
  190+         2    9
require(sjlabelled)

AERNASE.clin.hdac9$risk614 <- to_factor(AERNASE.clin.hdac9$risk614)

# Fix plaquephenotypes
attach(AERNASE.clin.hdac9)
AERNASE.clin.hdac9[,"Hypercholesterolemia"] <- NA
AERNASE.clin.hdac9$Hypercholesterolemia[risk614 == "missing value"] <- NA
AERNASE.clin.hdac9$Hypercholesterolemia[risk614 == -999] <- NA
AERNASE.clin.hdac9$Hypercholesterolemia[risk614 == 0] <- "no"
AERNASE.clin.hdac9$Hypercholesterolemia[risk614 == 1] <- "yes"
detach(AERNASE.clin.hdac9)

table(AERNASE.clin.hdac9$risk614, AERNASE.clin.hdac9$Hypercholesterolemia)
< table of extent 3 x 0 >
# AEDB.temp <- subset(AEDB,  select = c("STUDY_NUMBER", "UPID", "Age", "Gender", "Hospital", "Artery_summary", "risk614", "Hypercholesterolemia"))
# require(labelled)
# AEDB.temp$Gender <- to_factor(AEDB.temp$Gender)
# AEDB.temp$Hospital <- to_factor(AEDB.temp$Hospital)
# AEDB.temp$Artery_summary <- to_factor(AEDB.temp$Artery_summary)
# 
# DT::datatable(AEDB.temp[1:10,], caption = "Excerpt of the whole AEDB.", rownames = FALSE)
# 
# rm(AEDB.temp)

Now we can draw some graphs of plaque target(s) levels per sex and hypercholesterolemia/LDL-levels group, as well as stratified by lipid-lowering drugs users as median ± interquartile range.


compare_means(HDAC9 ~ LDLGroup, data = AERNASE.clin.hdac9 %>% filter(!is.na(LDLGroup)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(LDLGroup)), 
                  x = c("LDLGroup"),
                  y = "HDAC9", 
                  xlab = "LDL (mg/dL) per gender",
                  ylab = "HDAC9 (normalized expression))",
                  color = "LDLGroup",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.LDLGroups.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ LDLGroup, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(LDLGroup)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(LDLGroup)), 
                  x = c("LDLGroup"),
                  y = "HDAC9", 
                  xlab = "LDL (mg/dL) per gender",
                  ylab = "HDAC9 (normalized expression))",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.LDLGroups_byGender.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ Hypercholesterolemia, data = AERNASE.clin.hdac9 %>% filter(!is.na(Hypercholesterolemia)), method = "kruskal.test")
Error in kruskal.test.default(x = mf[[1L]], g = mf[[2L]]) : 
  all observations are in the same group
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Hypercholesterolemia)), 
                  x = c("Hypercholesterolemia"),
                  y = "HDAC9", 
                  xlab = "Diagnosed hypercholesterolemia",
                  ylab = "HDAC9 (normalized expression))",
                  color = "Hypercholesterolemia",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Hypercholesterolemia.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ Hypercholesterolemia, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(Hypercholesterolemia)), method = "kruskal.test")
Error in names(grouped.d$data) <- .names : 
  'names' attribute [1] must be the same length as the vector [0]
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Hypercholesterolemia)), 
                  x = c("Hypercholesterolemia"),
                  y = "HDAC9", 
                  xlab = "Diagnosed hypercholesterolemia per gender",
                  ylab = "HDAC9 (normalized expression))",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Hypercholesterolemia_byGender.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ Med.Statin.LLD, data = AERNASE.clin.hdac9 %>% filter(!is.na(Med.Statin.LLD)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Med.Statin.LLD)), 
                  x = c("Med.Statin.LLD"),
                  y = "HDAC9", 
                  xlab = "Lipid-lowering drug use",
                  ylab = "HDAC9 (normalized expression))",
                  color = "Med.Statin.LLD",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Med.Statin.LLD.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ Med.Statin.LLD, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(Med.Statin.LLD)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Med.Statin.LLD)), 
                  x = c("Med.Statin.LLD"),
                  y = "HDAC9", 
                  xlab = "Lipid-lowering drug use per gender",
                  ylab = "HDAC9 (normalized expression))",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Med.Statin.LLD_byGender.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ LDLGroup, group.by = "Med.Statin.LLD", data = AERNASE.clin.hdac9 %>% filter(!is.na(LDLGroup) & !is.na(Med.Statin.LLD)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(LDLGroup) & !is.na(Med.Statin.LLD)), 
                  x = c("LDLGroup"),
                  y = "HDAC9", 
                  xlab = "LDL (mg/dL) per LLD use",
                  ylab = "HDAC9 (normalized expression))",
                  color = "Med.Statin.LLD",
                  palette = c("#49A01D", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Med.Statin.LLD), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.LDLGroups_byMed.Statin.LLD.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ Hypercholesterolemia, group.by = "Med.Statin.LLD", data = AERNASE.clin.hdac9 %>% filter(!is.na(Hypercholesterolemia) & !is.na(Med.Statin.LLD)), method = "kruskal.test")
Error in names(grouped.d$data) <- .names : 
  'names' attribute [1] must be the same length as the vector [0]
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Hypercholesterolemia) & !is.na(Med.Statin.LLD)), 
                  x = c("Hypercholesterolemia"),
                  y = "HDAC9", 
                  xlab = "Diagnosed hypercholesterolemia per LLD use",
                  ylab = "HDAC9 (normalized expression))",
                  color = "Med.Statin.LLD",
                  palette = c("#49A01D", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Med.Statin.LLD), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.LDLGroups_byMed.Statin.LLD.pdf"), plot = last_plot())
Saving 12 x 8 in image

Kidney function (eGFR)

We want to create figures of target(s) levels stratified by kidney function.

  • Box and Whisker plot for target(s) plaque levels by chronic kidney disease (CKD) group (1, 2, 3, 4, 5)
  • Box and Whisker plot for target(s) plaque levels by eGFR (MDRD-based) group (90+, 60-89, 30-59, <30)
library(dplyr)

AERNASE.clin.hdac9 <- AERNASE.clin.hdac9 %>% mutate(eGFRGroup = factor(case_when(GFR_MDRD < 15 ~ "<15",
                                                             GFR_MDRD >= 15  & GFR_MDRD <= 29 ~ "15-29",
                                                             GFR_MDRD >= 30  & GFR_MDRD <= 59 ~ "30-59",
                                                             GFR_MDRD >= 60  & GFR_MDRD <= 89 ~ "60-89",
                                                             GFR_MDRD >= 90 ~ "90+")))

table(AERNASE.clin.hdac9$eGFRGroup, AERNASE.clin.hdac9$Gender)
       
        female male
  15-29      2    6
  30-59     38  101
  60-89     84  249
  90+       25   86
table(AERNASE.clin.hdac9$eGFRGroup, AERNASE.clin.hdac9$KDOQI)
       
        No data available/missing Normal kidney function CKD 2 (Mild) CKD 3 (Moderate) CKD 4 (Severe) CKD 5 (Failure)
  15-29                         0                      0            0                0              8               0
  30-59                         0                      0            0              139              0               0
  60-89                         0                      0          333                0              0               0
  90+                           0                    111            0                0              0               0

Now we can draw some graphs of plaque target(s) levels per sex and kidney function group as median ± interquartile range.


# Global test

compare_means(HDAC9 ~ eGFRGroup, data = AERNASE.clin.hdac9 %>% filter(!is.na(eGFRGroup)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(eGFRGroup)), 
                  x = c("eGFRGroup"),
                  y = "HDAC9", 
                  xlab = "eGFR (mL/min per 1.73 m2)",
                  ylab = "HDAC9 (normalized expression)",
                  color = "eGFRGroup",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.EGFR.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ eGFRGroup, group.by = "Gender",  data = AERNASE.clin.hdac9 %>% filter(!is.na(eGFRGroup)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(eGFRGroup)), 
                  x = c("eGFRGroup"),
                  y = "HDAC9", 
                  xlab = "eGFR (mL/min per 1.73 m2) per gender",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.EGFR_byGender.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ KDOQI, data = AERNASE.clin.hdac9 %>% filter(!is.na(KDOQI)), method = "kruskal.test")
p1 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(KDOQI)), 
                  x = c("KDOQI"),
                  y = "HDAC9", 
                  xlab = "Kidney function (KDOQI)",
                  ylab = "HDAC9 (normalized expression)",
                  color = "KDOQI",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(aes(group = KDOQI), label = "p.format", method = "kruskal.test")
ggpar(p1 + rotate_x_text(45), legend = "right") 

rm(p1)
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.KDOQI.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ KDOQI, group.by = "Gender",   data = AERNASE.clin.hdac9 %>% filter(!is.na(KDOQI)), method = "kruskal.test")
p1 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(KDOQI)), 
                  x = c("KDOQI"),
                  y = "HDAC9", 
                  xlab = "Kidney function (KDOQI) per gender",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
ggpar(p1 + rotate_x_text(45), legend = "right") 

rm(p1)
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.KDOQI_byGender.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ eGFRGroup,  data = AERNASE.clin.hdac9 %>% filter(!is.na(eGFRGroup) & !is.na(KDOQI)), method = "kruskal.test")
p1 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(eGFRGroup) & !is.na(KDOQI)), 
                  x = c("eGFRGroup"),
                  y = "HDAC9", 
                  xlab = "eGFR (mL/min per 1.73 m2) by KDOQI group",
                  ylab = "HDAC9 (normalized expression)",
                  color = "KDOQI",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(method = "kruskal.test")
ggpar(p1, legend = "right")

rm(p1)
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.EGFR_KDOQI.pdf"), plot = last_plot())
Saving 12 x 8 in image

BMI

We want to create figures of target(s) levels stratified by BMI.

  • Box and Whisker plot for target(s) plaque levels by BMI WHO group (underweight, normal, overweight, obese)
  • Box and Whisker plot for target(s) plaque levels by BMI group (<18.5, 18.5-24.9, 25, 29.9, 30-24.9, 35+)
library(dplyr)

AERNASE.clin.hdac9 <- AERNASE.clin.hdac9 %>% mutate(BMIGroup = factor(case_when(BMI < 18.5 ~ "<18.5",
                                                     BMI >= 18.5  & BMI < 25 ~ "18.5-24",
                                                     BMI >= 25  & BMI < 30 ~ "25-29",
                                                     BMI >= 30  & BMI < 35 ~ "30-35",
                                                     BMI >= 35 ~ "35+"))) 

# require(labelled)
# AERNASE.clin.hdac9$BMI_US <- as_factor(AERNASE.clin.hdac9$BMI_US)
# AERNASE.clin.hdac9$BMI_WHO <- as_factor(AERNASE.clin.hdac9$BMI_WHO)
# table(AERNASE.clin.hdac9$BMI_WHO, AERNASE.clin.hdac9$BMI_US)

table(AERNASE.clin.hdac9$BMIGroup, AERNASE.clin.hdac9$Gender)
         
          female male
  <18.5        3    2
  18.5-24     46  162
  25-29       68  220
  30-35       18   55
  35+          6   12
table(AERNASE.clin.hdac9$BMIGroup, AERNASE.clin.hdac9$BMI_WHO)
         
          No data available/missing Underweight Normal Overweight Obese
  <18.5                           0           5      0          0     0
  18.5-24                         0           0    208          0     0
  25-29                           0           0      0        287     0
  30-35                           0           0      0          0    73
  35+                             0           0      0          0    18

Now we can draw some graphs of plaque MCP1 levels per sex and age group as median ± interquartile range.


# Global test
compare_means(HDAC9 ~ BMIGroup,  data = AERNASE.clin.hdac9 %>% filter(!is.na(BMIGroup)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(BMIGroup)), 
                  x = c("BMIGroup"),
                  y = "HDAC9", 
                  xlab = "BMI groups (kg/m2)",
                  ylab = "HDAC9 (normalized expression)",
                  # color = "Gender",
                  # palette = c("#D5267B", "#1290D9"),
                  color = "BMIGroup",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.BMI.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ BMIGroup, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(BMIGroup)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(BMIGroup)), 
                  x = c("BMIGroup"),
                  y = "HDAC9", 
                  xlab = "BMI groups (kg/m2) per gender",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.BMI_byGender.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ BMIGroup,  data = AERNASE.clin.hdac9 %>% filter(!is.na(BMIGroup) & !is.na(BMI_WHO)), method = "kruskal.test")
p1 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(BMIGroup) & !is.na(BMI_WHO)), 
                  x = c("BMIGroup"),
                  y = "HDAC9", 
                  xlab = "BMI groups (kg/m2) per WHO categories",
                  ylab = "HDAC9 (normalized expression)",
                  color = "BMI_WHO",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(method = "kruskal.test")
ggpar(p1, legend = "right")

rm(p1)
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.BMI_byWHO.pdf"), plot = last_plot())
Saving 12 x 8 in image

Diabetes

We want to create figures of target(s) levels stratified by type 2 diabetes.

  • Box and Whisker plot for target(s) plaque levels by type 2 diabetes group (no, yes)

Now we can draw some graphs of plaque target(s) levels per sex and age group as median ± interquartile range.


# Global test
compare_means(HDAC9 ~ DiabetesStatus,  data = AERNASE.clin.hdac9 %>% filter(!is.na(DiabetesStatus)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(DiabetesStatus)), 
                  x = c("DiabetesStatus"),
                  y = "HDAC9", 
                  xlab = "Diabetes status",
                  ylab = "HDAC9 (normalized expression)",
                  # color = "Gender",
                  # palette = c("#D5267B", "#1290D9"),
                  color = "DiabetesStatus",
                  palette = "npg",
                  add = c("median_iqr", "jitter")) +
  stat_compare_means(label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Diabetes.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ DiabetesStatus, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(DiabetesStatus)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(DiabetesStatus)), 
                  x = c("DiabetesStatus"),
                  y = "HDAC9", 
                  xlab = "Diabetes status per gender",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = c("median_iqr", "jitter")) +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Diabetes_byGender.pdf"), plot = last_plot())
Saving 12 x 8 in image

Smoking

We want to create figures of target(s) levels stratified by smoking.

  • Box and Whisker plot for target(s) plaque levels by smoking group (never, ex, current)

Now we can draw some graphs of plaque target(s) levels per sex and age group as median ± interquartile range.


# Global test
compare_means(HDAC9 ~ SmokerStatus,  data = AERNASE.clin.hdac9 %>% filter(!is.na(SmokerStatus)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(SmokerStatus)), 
                  x = c("SmokerStatus"),
                  y = "HDAC9", 
                  xlab = "Smoker status",
                  ylab = "HDAC9 (normalized expression)",
                  # color = "Gender",
                  # palette = c("#D5267B", "#1290D9"),
                  color = "SmokerStatus",
                  palette = "npg",
                  add = c("median_iqr", "jitter")) +
  stat_compare_means(label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Smoking.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ SmokerStatus, group.by ="Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(SmokerStatus)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(SmokerStatus)), 
                  x = c("SmokerStatus"),
                  y = "HDAC9", 
                  xlab = "Smoker status per gender",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = c("median_iqr", "jitter")) +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Smoking_byGender.pdf"), plot = last_plot())
Saving 12 x 8 in image

Stenosis

We want to create figures of target(s) levels stratified by stenosis grade.

  • Box and Whisker plot for target(s) plaque levels by stenosis grade group (<70, 70-89, 90+)
library(dplyr)

AERNASE.clin.hdac9 <- AERNASE.clin.hdac9 %>% mutate(StenoticGroup = factor(case_when(stenose == "0-49%" ~ "<70",
                                                     stenose == "0-49%" ~ "<70",
                                                     stenose == "50-70%" ~ "<70",
                                                     stenose == "70-90%" ~ "70-89",
                                                     stenose == "50-99%" ~ "90+",
                                                     stenose == "70-99%" ~ "90+",
                                                     stenose == "100% (Occlusion)" ~ "90+",
                                                     stenose == "90-99%" ~ "90+")))

table(AERNASE.clin.hdac9$StenoticGroup, AERNASE.clin.hdac9$Gender)
       
        female male
  <70        6   34
  70-89     72  199
  90+       69  221
table(AERNASE.clin.hdac9$stenose, AERNASE.clin.hdac9$StenoticGroup)
                  
                   <70 70-89 90+
  missing            0     0   0
  0-49%              2     0   0
  50-70%            38     0   0
  70-90%             0   271   0
  90-99%             0     0 284
  100% (Occlusion)   0     0   5
  NA                 0     0   0
  50-99%             0     0   1
  70-99%             0     0   0
  99                 0     0   0

Now we can draw some graphs of plaque target(s) levels per sex and age group as median ± interquartile range.


# Global test
compare_means(HDAC9 ~ StenoticGroup,  data = AERNASE.clin.hdac9 %>% filter(!is.na(StenoticGroup)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(StenoticGroup)), 
                  x = c("StenoticGroup"),
                  y = "HDAC9", 
                  xlab = "Stenotic grade",
                  ylab = "HDAC9 (normalized expression)",
                  # color = "Gender",
                  # palette = c("#D5267B", "#1290D9"),
                  color = "StenoticGroup",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Stenosis.pdf"), plot = last_plot())
Saving 12 x 8 in image
compare_means(HDAC9 ~ StenoticGroup, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(StenoticGroup)), method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(StenoticGroup)), 
                  x = c("StenoticGroup"),
                  y = "HDAC9", 
                  xlab = "Stenotic grade per gender",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.Stenosis_byGender.pdf"), plot = last_plot())
Saving 12 x 8 in image

Symptoms

We want to create per-symptom figures.

library(dplyr)

table(AERNASE.clin.hdac9$AgeGroup, AERNASE.clin.hdac9$AsymptSympt2G)
       
        Asymptomatic Symptomatic
  <55             10          28
  55-64           24         143
  65-74           30         219
  75-84           15         141
  85+              1          12
table(AERNASE.clin.hdac9$Gender, AERNASE.clin.hdac9$AsymptSympt2G)
        
         Asymptomatic Symptomatic
  female           15         138
  male             65         405
table(AERNASE.clin.hdac9$AsymptSympt2G)

Asymptomatic  Symptomatic 
          80          543 

Now we can draw some graphs of plaque target(s) levels per symptom group as median ± interquartile range.


# ?ggpubr::ggboxplot()
my_comparisons <- list(c("Asymptomatic", "Symptomatic"))

compare_means(HDAC9 ~ AsymptSympt2G, data = AERNASE.clin.hdac9, method = "kruskal.test")
p1 <- ggpubr::ggboxplot(AERNASE.clin.hdac9, 
                  x = "AsymptSympt2G", y = "HDAC9",
                  title = "HDAC9 (normalized expression) levels per symptom", 
                  xlab = "Symptoms",
                  ylab = "HDAC9 (normalized expression)",
                  color = "AsymptSympt2G", 
                  # palette = c(uithof_color[16], uithof_color[23]),
                  palette = "npg",
                  add = "dotplot", # Add dotplot
                  add.params = list(binwidth = 0.1, dotsize = 0.3)
          ) +
  stat_compare_means(comparisons = my_comparisons, method = "wilcox.test")
ggpar(p1, legend = c("right"), legend.title = "Symptoms")


ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.AsymptSympt2G.pdf"), plot = last_plot())
Saving 12 x 8 in image
rm(p1)
compare_means(HDAC9 ~ AsymptSympt2G, group.by = "Gender", data = AERNASE.clin.hdac9, method = "kruskal.test")
p1 <- ggpubr::ggboxplot(AERNASE.clin.hdac9, 
                  x = "AsymptSympt2G", y = "HDAC9",
                  title = "HDAC9 (normalized expression) levels per symptom by gender", 
                  xlab = "Symptoms",
                  ylab = "HDAC9 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "dotplot", # Add dotplot
                  add.params = list(binwidth = 0.1, dotsize = 0.3)
          ) +
  stat_compare_means(aes(group = Gender), label = "p.format",  method = "wilcox.test")
ggpar(p1, legend = c("right"), legend.title = "Symptoms")


ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.AsymptSympt2G.byGender.pdf"), plot = last_plot())
Saving 12 x 8 in image
rm(p1)

Alternative graph

# ?ggpubr::ggboxplot()
# compare_means(LRCH1 ~ eGFRGroup, data = AERNASE.clin %>% filter(!is.na(eGFRGroup)), method = "kruskal.test")
# ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(eGFRGroup))

cat("Get summary statistics for target:\n")
Get summary statistics for target:
summary(AERNASE.clin.hdac9$HDAC9)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00   11.00   21.00   28.32   37.00  449.00 
cat("\nCount number of values > 100 for target:\n")

Count number of values > 100 for target:
sum(AERNASE.clin.hdac9$HDAC9 > 100)
[1] 12
cat("\nSetting values > 100 for target to 100.\n")

Setting values > 100 for target to 100.
temp <- AERNASE.clin.hdac9 %>% 
  filter(!is.na(AsymptSympt2G))

temp$HDAC9[temp$HDAC9 > 100] <- 100

cat("Get summary statistics for target after fixing values:\n")
Get summary statistics for target after fixing values:
summary(temp$HDAC9)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00   11.00   21.00   26.71   37.00  100.00 
my_comparisons <- list(c("Asymptomatic", "Symptomatic"))

compare_means(HDAC9 ~ AsymptSympt2G, data = temp, method = "kruskal.test")

p1 <- ggpubr::ggboxplot(temp, 
                  x = "AsymptSympt2G", y = "HDAC9",
                  title = "HDAC9 (normalized expression) levels per symptom", 
                  xlab = "Symptoms",
                  ylab = "HDAC9 (normalized expression)",
                  color = "AsymptSympt2G", 
                  # palette = c(uithof_color[16], uithof_color[23]),
                  palette = "npg",
                  add = "jitter", # Add dotplot
                  add.params = list(binwidth = 0.1, dotsize = 0.3)
          ) +
  stat_compare_means(comparisons = my_comparisons, method = "wilcox.test")
ggpar(p1, legend = c("right"), legend.title = "Symptoms")


ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HDAC9.plaque.AsymptSympt2G.noNA_limitCount100.pdf"), plot = last_plot())
Saving 12 x 8 in image
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HDAC9.plaque.AsymptSympt2G.noNA_limitCount100.ps"), plot = last_plot())
Saving 12 x 8 in image
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HDAC9.plaque.AsymptSympt2G.noNA_limitCount100.png"), plot = last_plot())
Saving 12 x 8 in image
rm(p1, temp)

Forest plots

We would also like to visualize the multivariable analyses results.

library(ggplot2)
library(openxlsx)
model1_target <- read.xlsx(paste0(OUT_loc, "/", Today, ".AERNASE.clin.hdac9.Bin.Uni.",TRAIT_OF_INTEREST,".RANK.Symptoms.MODEL1.xlsx"))
model2_target <- read.xlsx(paste0(OUT_loc, "/", Today, ".AERNASE.clin.hdac9.Bin.Multi.",TRAIT_OF_INTEREST,".RANK.Symptoms.MODEL2.xlsx"))

model1_target$model <- "univariate"
model2_target$model <- "multivariate"

models_target <- rbind(model1_target, model2_target)
models_target

Forest plots.

dat <- data.frame(group = factor(c("Age, sex-adjusted", "Age, sex, and adjusted for risk factors"), 
                           
                           levels=c("Age, sex, and adjusted for risk factors", "Age, sex-adjusted")),
                  cen = c(models_target$OR[models_target$Predictor=="HDAC9"]),
                  low = c(models_target$low95CI[models_target$Predictor=="HDAC9"]),
                  high = c(models_target$up95CI[models_target$Predictor=="HDAC9"]))

fp <- ggplot(data = dat, aes(x = group, y = cen, ymin = low, ymax = high)) +
  geom_pointrange(linetype = 2, size = 1, colour = c("#1290D9", "#49A01D")) + 
  geom_hline(yintercept = 1, lty = 2) +  # add a dotted line at x=1 after flip
  coord_flip(ylim = c(0.8, 1.7)) +  # flip coordinates (puts labels on y axis)
  xlab("Model") + ylab("OR (95% CI) for symptomatic plaques") +
  ggtitle("Normalized HDAC9 expression") +
  theme_minimal()  # use a white background
print(fp)

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,".plaque.forest.pdf"), plot = fp)

rm(fp)

HDAC9 vs. cytokines plaque levels correlations

We will plot the correlations of other cytokine plaque levels to the MCP1 plaque levels. These include:

  • IL2
  • IL4
  • IL5
  • IL6
  • IL8
  • IL9
  • IL10
  • IL12
  • IL13
  • IL21
  • INFG
  • TNFA
  • MIF
  • MCP1
  • MIP1a
  • RANTES
  • MIG
  • IP10
  • Eotaxin1
  • TARC
  • PARC
  • MDC
  • OPG
  • sICAM1
  • VEGFA
  • TGFB

In addition we will look at three metalloproteinases which were measured using an activity assay.

  • MMP2
  • MMP8
  • MMP9

The proteins were measured using FACS and LUMINEX. Given the different platforms used (FACS vs. LUMINEX), we will inverse rank-normalize these variables as well to scale them to the same scale as the target(s)` plaque levels.

We will set the measurements that yielded ‘0’ to NA, as it is unlikely that any protein ever has exactly 0 copies. The ‘0’ yielded during the experiment are due to the limits of the detection.

Prepare data

# fix names
names(AEDB.CEA)[names(AEDB.CEA) == "VEFGA"] <- "VEGFA"

# fix names
names(AERNASE.clin.hdac9)[names(AERNASE.clin.hdac9) == "IL6"] <- "IL6rna"

cytokines <- c("IL2", "IL4", "IL5", "IL6", "IL8", "IL9", "IL10", "IL12", "IL13", "IL21", 
               "INFG", "TNFA", "MIF", "MCP1", "MIP1a", "RANTES", "MIG", "IP10", "Eotaxin1", 
               "TARC", "PARC", "MDC", "OPG", "sICAM1", "VEGFA", "TGFB")
metalloproteinases <- c("MMP2", "MMP8", "MMP9")


AERNASE.clin.hdac9 <- merge(AERNASE.clin.hdac9, 
                            subset(AEDB.CEA, select = c("STUDY_NUMBER", 
                                                        cytokines,
                                                        metalloproteinases)), 
                            by.x = "STUDY_NUMBER", by.y = "STUDY_NUMBER", sort = TRUE, all.x = TRUE)

proteins_of_interest <- c(cytokines, metalloproteinases)

proteins_of_interest_rank = unlist(lapply(proteins_of_interest, paste0, "_rank"))

# make variables numerics()
AERNASE.clin.hdac9 <- AERNASE.clin.hdac9 %>%
  mutate_each(funs(as.numeric), proteins_of_interest)
  
for(PROTEIN in 1:length(proteins_of_interest)){

  # UCORBIOGSAqc$Z <- NULL
  var.temp.rank = proteins_of_interest_rank[PROTEIN]
  var.temp = proteins_of_interest[PROTEIN]
  
  cat(paste0("\nSelecting ", var.temp, " and standardising: ", var.temp.rank,".\n"))
  cat(paste0("* changing ", var.temp, " to numeric.\n"))

  # AERNASE.clin.hdac9 <-  AERNASE.clin.hdac9 %>% mutate(AERNASE.clin.hdac9[,var.temp] == replace(AERNASE.clin.hdac9[,var.temp], AERNASE.clin.hdac9[,var.temp]==0, NA))

  AERNASE.clin.hdac9[,var.temp][AERNASE.clin.hdac9[,var.temp]==0.000000]=NA

  cat(paste0("* standardising ", var.temp, 
             " (mean: ",round(mean(!is.na(AERNASE.clin.hdac9[,var.temp])), digits = 6),
             ", n = ",sum(!is.na(AERNASE.clin.hdac9[,var.temp])),").\n"))
  
  AERNASE.clin.hdac9 <- AERNASE.clin.hdac9 %>%
      mutate_at(vars(var.temp), 
        # list(Z = ~ (AERNASE.clin.hdac9[,var.temp] - mean(AERNASE.clin.hdac9[,var.temp], na.rm = TRUE))/sd(AERNASE.clin.hdac9[,var.temp], na.rm = TRUE))
        list(RANK = ~ qnorm((rank(AERNASE.clin.hdac9[,var.temp], na.last = "keep") - 0.5) / sum(!is.na(AERNASE.clin.hdac9[,var.temp]))))
      )
  # str(UCORBIOGSAqc$Z)
  cat(paste0("* renaming RANK to ", var.temp.rank,".\n"))
  AERNASE.clin.hdac9[,var.temp.rank] <- NULL
  names(AERNASE.clin.hdac9)[names(AERNASE.clin.hdac9) == "RANK"] <- var.temp.rank
}

# rm(var.temp, var.temp.rank)

Visualize transformations

We will just visualize these transformations.

proteins_of_interest_rank_target <- c("HDAC9", proteins_of_interest_rank)

proteins_of_interest_target <- c("HDAC9", proteins_of_interest)

for(PROTEIN in proteins_of_interest_target){
  cat(paste0("Plotting protein ", PROTEIN, ".\n"))
  
  p1 <- ggpubr::gghistogram(AERNASE.clin.hdac9, PROTEIN,
                    # y = "..count..",
                    color = "white",
                    fill = "Gender",
                    palette = c("#1290D9", "#DB003F"),
                    add = "mean",
                    # rug = TRUE,
                    # add.params =  list(color = "black", linetype = 2),
                    title = paste0(PROTEIN, " plaque levels"),
                    xlab = "",
                    ggtheme = theme_minimal())
  print(p1)
  
}


for(PROTEIN in proteins_of_interest_rank_target){
  cat(paste0("Plotting protein ", PROTEIN, ".\n"))
  
  p1 <- ggpubr::gghistogram(AERNASE.clin.hdac9, PROTEIN,
                    # y = "..count..",
                    color = "white",
                    fill = "Gender",
                    palette = c("#1290D9", "#DB003F"),
                    add = "mean",
                    # rug = TRUE,
                    # add.params =  list(color = "black", linetype = 2),
                    title = paste0(PROTEIN, " plaque levels"),
                    xlab = "inverse-normal transformation",
                    ggtheme = theme_minimal())
  print(p1)
  
}
  

Correlations

Here we calculate correlations between target(s) and 28 other cytokines. We use Spearman’s test, thus, correlations a given in rho. Please note the indications of measurement methods:

  • L: LUMINEX
  • E: ELISA
  • a: activity assay
# Installation of ggcorrplot()
# --------------------------------
if(!require(devtools)) 
  install.packages("devtools")
devtools::install_github("kassambara/ggcorrplot")

library(ggcorrplot)

# Creating matrix - inverse-rank transformation
# --------------------------------
temp <- subset(AERNASE.clin.hdac9, 
                          select = c(proteins_of_interest_rank_target)
                                    )

# str(AEDB.CEA.temp)
matrix.RANK <- as.matrix(temp)
rm(temp)

corr_biomarkers.rank <- round(cor(matrix.RANK, 
                             use = "pairwise.complete.obs", #the correlation or covariance between each pair of variables is computed using all complete pairs of observations on those variables
                             method = "spearman"), 3)
# corr_biomarkers.rank

rename_proteins_of_interest_target <- c("HDAC9 (RNA)", 
                                    "IL2", "IL4", "IL5", "IL6", "IL8", "IL9", "IL10", "IL12", 
                                    "IL13 (L)", "IL21 (L)", 
                                    "INFG", "TNFA", "MIF (L)", 
                                    "MCP1 (L)", "MIP1a (L)", "RANTES (L)", "MIG (L)", "IP10 (L)", 
                                    "Eotaxin1 (L)", "TARC (L)", "PARC (L)", "MDC (L)", 
                                    "OPG (L)", "sICAM1 (L)", "VEGFA (E)", "TGFB (E)", "MMP2 (a)", "MMP8 (a)", "MMP9 (a)")
colnames(corr_biomarkers.rank) <- c(rename_proteins_of_interest_target)
rownames(corr_biomarkers.rank) <- c(rename_proteins_of_interest_target)

corr_biomarkers_p.rank <- ggcorrplot::cor_pmat(matrix.RANK, use = "pairwise.complete.obs", method = "spearman")

# ++++++++++++++++++++++++++++
# flattenCorrMatrix
# ++++++++++++++++++++++++++++
# cormat : matrix of the correlation coefficients
# pmat : matrix of the correlation p-values
flattenCorrMatrix <- function(cormat, pmat) {
  ut <- upper.tri(cormat)
  data.frame(
    row = rownames(cormat)[row(cormat)[ut]],
    column = rownames(cormat)[col(cormat)[ut]],
    cor  =(cormat)[ut],
    p = pmat[ut]
    )
}

corr_biomarkers.rank.df <- flattenCorrMatrix(corr_biomarkers.rank, corr_biomarkers_p.rank)


names(corr_biomarkers.rank.df)[names(corr_biomarkers.rank.df) == "row"] <- "Cytokine_X"
names(corr_biomarkers.rank.df)[names(corr_biomarkers.rank.df) == "column"] <- "CytokineY"
names(corr_biomarkers.rank.df)[names(corr_biomarkers.rank.df) == "cor"] <- "SpearmanRho"

DT::datatable(corr_biomarkers.rank.df)

fwrite(corr_biomarkers.rank.df, file = paste0(OUT_loc, "/",Today,".correlation_cytokines.txt"))
# Add correlation coefficients
# --------------------------------
# argument lab = TRUE
p1 <- ggcorrplot(corr_biomarkers.rank, 
           method = "square", 
           type = "lower",
           title = "Cross biomarker correlations", 
           show.legend = TRUE, legend.title = bquote("Spearman's"~italic(rho)),
           ggtheme = ggplot2::theme_minimal, outline.color = "#FFFFFF",
           show.diag = TRUE,
           hc.order = FALSE, 
           lab = FALSE,
           digits = 3,
           tl.cex = 16,
           # xlab = c("MCP1"),
           # p.mat = corr_biomarkers_p.rank, sig.level = 0.05,
           colors = c("#1290D9", "#FFFFFF", "#E55738"))
p1
ggsave(filename = paste0(PLOT_loc, "/", Today, ".correlation_cytokines.png"), plot = last_plot())
ggsave(filename = paste0(PLOT_loc, "/", Today, ".correlation_cytokines.pdf"), plot = last_plot())

rm(p1)

While visually attractive we are not necessarily interested in the correlations between all the cytokines, rather of target(s)` with other cytokines only.

temp <- subset(corr_biomarkers.rank.df, Cytokine_X == "HDAC9 (RNA)" )
temp$p_log10 <- -log10(temp$p)
p_threshold <- -log10(0.05/nrow(temp))
p_threshold
p1 <- ggbarplot(temp, x = "CytokineY", y = "SpearmanRho",
          fill = "CytokineY",               # change fill color by cyl
          # color = "white",            # Set bar border colors to white
          palette = uithof_color,            # jco journal color palett. see ?ggpar
          xlab = "Cytokine",
          ylab = expression("Spearman's"~italic(rho)),
          sort.val = "desc",          # Sort the value in dscending order
          sort.by.groups = FALSE,     # Don't sort inside each group
          x.text.angle = 45, # Rotate vertically x axis texts
          cex = 1.25
          )
ggpar(p1, legend = "bottom", 
      legend.title = "") +
  theme(axis.text.x = element_text(size = 14),
        axis.text.y = element_text(size = 14),
        axis.title.x = element_text(size = 18),
        axis.title.y = element_text(size = 18)) 

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,"_vs_Cytokines.png"), plot = last_plot())
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,"_vs_Cytokines.pdf"), plot = last_plot())
rm(p1)

Another version - probably not good.

temp <- subset(corr_biomarkers.rank.df, Cytokine_X == "HDAC9 (RNA)" )
temp$p_log10 <- -log10(temp$p)
p_threshold <- -log10(0.05/nrow(temp))
p_threshold
p1 <- ggdotchart(temp, x = "CytokineY", y = "p_log10",
           color = "CytokineY", #fill = "CytokineY",                              # Color by groups
           palette = uithof_color, # Custom color palette
           xlab = "Cytokine",
           ylab = expression(log[10]~"("~italic(p)~")-value"),
           # ylim = c(0, 9),
           sorting = "descending",                       # Sort value in descending order
           add = "segments",                             # Add segments from y = 0 to dots
           rotate = FALSE,                                # Rotate vertically
           # group = "CytokineY",                                # Order by groups
           dot.size = 16,                                 # Large dot size
           label = round(temp$SpearmanRho, digits = 3),                        # Add mpg values as dot labels
           font.label = list(color = "white", size = 12, 
                             vjust = 0.5)                   
           )
ggpar(p1, legend = "", 
      legend.title = "") +
  theme(axis.text.x = element_text(size = 14),
        axis.text.y = element_text(size = 14),
        axis.title.x = element_text(size = 18),
        axis.title.y = element_text(size = 18))

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,"_vs_Cytokines.dotchart.png"), plot = last_plot())
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.hdac9.",TRAIT_OF_INTEREST,"_vs_Cytokines.dotchart.pdf"), plot = last_plot())

rm(temp, p1)

HDAC9 vs. cytokines plaque levels

Model 1

In this model we correct for Age, Gender, and year of surgery.

Here we use the inverse-rank normalized data - visually this is more normally distributed.

Analysis of plaque cytokines traits as a function of plaque target(s) levels.


GLM.results <- data.frame(matrix(NA, ncol = 15, nrow = 0))
cat("Running linear regression...\n")
for (protein in 1:length(TRAITS.TARGET.RANK)) {
  PROTEIN = TRAITS.TARGET.RANK[protein]
  cat(paste0("\nAnalysis of ",PROTEIN,".\n"))
  for (trait in 1:length(proteins_of_interest_rank)) {
    TRAIT = proteins_of_interest_rank[trait]
    cat(paste0("\n- processing ",TRAIT,"\n\n"))
    currentDF <- as.data.frame(AERNASE.clin.hdac9 %>%
      dplyr::select(., PROTEIN, TRAIT, COVARIATES_M1) %>%
      filter(complete.cases(.))) %>%
      filter_if(~is.numeric(.), all_vars(!is.infinite(.)))
    # for debug
    # print(DT::datatable(currentDF))
    # print(nrow(currentDF))
    # print(str(currentDF))
    ### univariate
    # fit <- lm(currentDF[,PROTEIN] ~ currentDF[,TRAIT] + Age + Gender + ORdate_year, data = currentDF)
    fit <- lm(currentDF[,PROTEIN] ~ currentDF[,TRAIT] + Age + Gender + ORdate_epoch, data = currentDF)
    model_step <- stepAIC(fit, direction = "both", trace = FALSE)
    print(model_step)
    print(summary(fit))

    GLM.results.TEMP <- data.frame(matrix(NA, ncol = 15, nrow = 0))
    GLM.results.TEMP[1,] = GLM.CON(fit, "AEDB.CEA", PROTEIN, TRAIT, verbose = TRUE)
    GLM.results = rbind(GLM.results, GLM.results.TEMP)
  }
}
cat("Edit the column names...\n")
colnames(GLM.results) = c("Dataset", "Predictor", "Trait",
                          "Beta", "s.e.m.",
                          "OR", "low95CI", "up95CI",
                          "T-value", "P-value", "r^2", "r^2_adj", "N", "Model_N", "Perc_Miss")

cat("Correct the variable types...\n")
GLM.results$Beta <- as.numeric(GLM.results$Beta)
GLM.results$s.e.m. <- as.numeric(GLM.results$s.e.m.)
GLM.results$OR <- as.numeric(GLM.results$OR)
GLM.results$low95CI <- as.numeric(GLM.results$low95CI)
GLM.results$up95CI <- as.numeric(GLM.results$up95CI)
GLM.results$`T-value` <- as.numeric(GLM.results$`T-value`)
GLM.results$`P-value` <- as.numeric(GLM.results$`P-value`)
GLM.results$`r^2` <- as.numeric(GLM.results$`r^2`)
GLM.results$`r^2_adj` <- as.numeric(GLM.results$`r^2_adj`)
GLM.results$`N` <- as.numeric(GLM.results$`N`)
GLM.results$`Model_N` <- as.numeric(GLM.results$`Model_N`)
GLM.results$`Perc_Miss` <- as.numeric(GLM.results$`Perc_Miss`)
DT::datatable(GLM.results)

# Save the data
cat("Writing results to Excel-file...\n")
### Univariate
library(openxlsx)
write.xlsx(GLM.results,
           file = paste0(OUT_loc, "/",Today,".AERNASE.clin.hdac9.Con.Uni.",TRAIT_OF_INTEREST,"_Plaque.Cytokines_Plaques.RANK.MODEL1.xlsx"),
           rowNmes = FALSE, colNames = TRUE, sheetName = "Con.Uni.PlaquePheno")
# Removing intermediates
cat("Removing intermediate files...\n")
rm(TRAIT, trait, currentDF, GLM.results, GLM.results.TEMP, fit, model_step)

Model 2

In this model we correct for Age, Gender, year of surgery, Hypertension status, Diabetes status, current smoker status, lipid-lowering drugs (LLDs), antiplatelet medication, eGFR (MDRD), BMI, MedHx_CVD (combination of CAD history, stroke history, and peripheral interventions), and stenosis.

Here we use the inverse-rank normalized data - visually this is more normally distributed.

Analysis of plaque cytokines as a function of plaque target(s) levels.


GLM.results <- data.frame(matrix(NA, ncol = 15, nrow = 0))
cat("Running linear regression...\n")
for (protein in 1:length(TRAITS.TARGET.RANK)) {
  PROTEIN = TRAITS.TARGET.RANK[protein]
  cat(paste0("\nAnalysis of ",PROTEIN,".\n"))
  for (trait in 1:length(proteins_of_interest_rank)) {
    TRAIT = proteins_of_interest_rank[trait]
    cat(paste0("\n- processing ",TRAIT,"\n\n"))
    currentDF <- as.data.frame(AERNASE.clin.hdac9 %>%
      dplyr::select(., PROTEIN, TRAIT, COVARIATES_M2) %>%
      filter(complete.cases(.))) %>%
      filter_if(~is.numeric(.), all_vars(!is.infinite(.)))
    # for debug
    # print(DT::datatable(currentDF))
    # print(nrow(currentDF))
    # print(str(currentDF))
    ### univariate
    # fit <- lm(currentDF[,PROTEIN] ~ currentDF[,TRAIT] + Age + Gender + ORdate_year + 
    #             Hypertension.composite + DiabetesStatus + SmokerStatus + 
    #             Med.Statin.LLD + Med.all.antiplatelet + GFR_MDRD + BMI + 
    #             MedHx_CVD + stenose, 
    #           data = currentDF)
    fit <- lm(currentDF[,PROTEIN] ~ currentDF[,TRAIT] + Age + Gender + ORdate_epoch + 
                Hypertension.composite + DiabetesStatus + SmokerStatus + 
                Med.Statin.LLD + Med.all.antiplatelet + GFR_MDRD + BMI + 
                MedHx_CVD + stenose, 
              data = currentDF)
    model_step <- stepAIC(fit, direction = "both", trace = FALSE)
    print(model_step)
    print(summary(fit))
    
    GLM.results.TEMP <- data.frame(matrix(NA, ncol = 15, nrow = 0))
    GLM.results.TEMP[1,] = GLM.CON(fit, "AEDB.CEA", PROTEIN, TRAIT, verbose = TRUE)
    GLM.results = rbind(GLM.results, GLM.results.TEMP)
  }
}
cat("Edit the column names...\n")
colnames(GLM.results) = c("Dataset", "Predictor", "Trait",
                          "Beta", "s.e.m.",
                          "OR", "low95CI", "up95CI",
                          "T-value", "P-value", "r^2", "r^2_adj", "N", "Model_N", "Perc_Miss")

cat("Correct the variable types...\n")
GLM.results$Beta <- as.numeric(GLM.results$Beta)
GLM.results$s.e.m. <- as.numeric(GLM.results$s.e.m.)
GLM.results$OR <- as.numeric(GLM.results$OR)
GLM.results$low95CI <- as.numeric(GLM.results$low95CI)
GLM.results$up95CI <- as.numeric(GLM.results$up95CI)
GLM.results$`T-value` <- as.numeric(GLM.results$`T-value`)
GLM.results$`P-value` <- as.numeric(GLM.results$`P-value`)
GLM.results$`r^2` <- as.numeric(GLM.results$`r^2`)
GLM.results$`r^2_adj` <- as.numeric(GLM.results$`r^2_adj`)
GLM.results$`N` <- as.numeric(GLM.results$`N`)
GLM.results$`Model_N` <- as.numeric(GLM.results$`Model_N`)
GLM.results$`Perc_Miss` <- as.numeric(GLM.results$`Perc_Miss`)
DT::datatable(GLM.results)

# Save the data
cat("Writing results to Excel-file...\n")
### Univariate
library(openxlsx)
write.xlsx(GLM.results,
           file = paste0(OUT_loc, "/",Today,".AERNASE.clin.hdac9.Con.Multi.",TRAIT_OF_INTEREST,"_Plaque.Cytokines_Plaques.RANK.MODEL2.xlsx"),
           rowNames = FALSE, colNames = TRUE, sheetName = "Con.Multi.PlaquePheno")
# Removing intermediates
cat("Removing intermediate files...\n")
rm(TRAIT, trait, currentDF, GLM.results, GLM.results.TEMP, fit, model_step)

HDAC9 levels vs. vulnerability index

Here we plot the levels of inverse-rank normal transformed target(s) plaque levels from experiment 1 and 2 to the Plaque vulnerability index.

library(sjlabelled)

AERNASE.clin.hdac9$yeartemp <- as.numeric(year(AERNASE.clin.hdac9$dateok))

attach(AERNASE.clin.hdac9)

AERNASE.clin.hdac9[,"ORyearGroup"] <- NA
AERNASE.clin.hdac9$ORyearGroup[yeartemp <= 2007] <- "< 2007"
AERNASE.clin.hdac9$ORyearGroup[yeartemp > 2007] <- "> 2007"
detach(AERNASE.clin.hdac9)

table(AERNASE.clin.hdac9$ORyearGroup, AERNASE.clin.hdac9$ORdate_year)

Visualisations

# Global test
compare_means(HDAC9 ~ Plaque_Vulnerability_Index,  data = AERNASE.clin.hdac9, method = "kruskal.test")
p1 <- ggpubr::ggboxplot(AERNASE.clin.hdac9, 
                  x = "Plaque_Vulnerability_Index",
                  y = "HDAC9", 
                  xlab = "Plaque vulnerability index",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Plaque_Vulnerability_Index",
                  palette = "npg",
                  add = "jitter", 
                  add.params = list(size = 2, jitter = 0.2)) +
  stat_compare_means(label = "p.format",  method = "kruskal.test") +
  font("xlab", size = 17) +
  font("ylab", size = 17) +
  font("xy.text", size = 16) +
  font("legend.title", face = "bold") 
  
ggpar(p1, legend = "bottom", legend.title = "Plaque vulnerability index")

ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.PlaqueVulnerabilityIndex.pdf"), plot = last_plot())
# Global test
compare_means(HDAC9 ~ Plaque_Vulnerability_Index,  data = AERNASE.clin.hdac9, method = "kruskal.test")

p1 <- ggpubr::ggbarplot(AERNASE.clin.hdac9, 
                  x = "Plaque_Vulnerability_Index",
                  y = "HDAC9", 
                  xlab = "Plaque vulnerability index",
                  ylab = "HDAC9 (normalized expression)\n",
                  col = "Plaque_Vulnerability_Index",
                  fill = "Plaque_Vulnerability_Index",
                  palette = "npg",
                  add = "median_iqr", error.plot = "upper_errorbar") +
  stat_compare_means(label = "p.format",  method = "kruskal.test",
                     label.x = 1, label.y = 50) +
  font("xlab", size = 17) +
  font("ylab", size = 17) +
  font("xy.text", size = 16) +
  font("legend.title", face = "bold") 
  
ggpar(p1, legend = "bottom", legend.title = "Plaque vulnerability index", ylim = c(0, 55))

ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.PlaqueVulnerabilityIndex.BarPlot.median_iqr.pdf"), plot = last_plot())

compare_means(HDAC9 ~ Plaque_Vulnerability_Index,  data = AERNASE.clin.hdac9, method = "kruskal.test")
p1 <- ggpubr::ggbarplot(AERNASE.clin.hdac9, 
                  x = "Plaque_Vulnerability_Index",
                  y = "HDAC9", 
                  xlab = "Plaque vulnerability index",
                  ylab = "HDAC9 (normalized expression)\n",
                  col = "Plaque_Vulnerability_Index",
                  fill = "Plaque_Vulnerability_Index",
                  palette = "npg",
                  add = "mean_se", error.plot = "upper_errorbar") +
  stat_compare_means(label = "p.format",  method = "kruskal.test",
                     label.x = 1, label.y = 50) +
  font("xlab", size = 17) +
  font("ylab", size = 17) +
  font("xy.text", size = 16) +
  font("legend.title", face = "bold") 
  
ggpar(p1, legend = "bottom", legend.title = "Plaque vulnerability index", ylim = c(0, 55))

ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.PlaqueVulnerabilityIndex.BarPlot.means_se.pdf"), plot = last_plot())
compare_means(HDAC9 ~ Plaque_Vulnerability_Index,  data = subset(AERNASE.clin.hdac9, HDAC9 <100), method = "kruskal.test")

p1 <- ggpubr::ggboxplot(subset(AERNASE.clin.hdac9, HDAC9 <100) , 
                  x = "Plaque_Vulnerability_Index",
                  y = "HDAC9", 
                  xlab = "Plaque vulnerability index",
                  ylab = "HDAC9 (normalized expression)\noutliers above 100 are removed",
                  col = "Plaque_Vulnerability_Index",
                  fill = "Plaque_Vulnerability_Index",
                  palette = "npg",
                  add = "boxplot", error.plot = "crossbar") +
  stat_compare_means(label = "p.format",  method = "kruskal.test",
                     label.x = 1, label.y = 50) +
  font("xlab", size = 17) +
  font("ylab", size = 17) +
  font("xy.text", size = 16) +
  font("legend.title", face = "bold") 
  
ggpar(p1, legend = "bottom", legend.title = "Plaque vulnerability index")

ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.PlaqueVulnerabilityIndex.Boxplot.outlier_above_100_removed.pdf"), plot = last_plot())
compare_means(HDAC9 ~ Plaque_Vulnerability_Index,  data = AERNASE.clin.hdac9, method = "kruskal.test")
p1 <- ggpubr::ggboxplot(AERNASE.clin.hdac9, 
                  x = "Plaque_Vulnerability_Index",
                  y = "HDAC9", 
                  facet.by = "Plaque_Vulnerability_Index",
                  xlab = "Plaque vulnerability index",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Plaque_Vulnerability_Index",
                  palette = "npg",
                  add = "jitter", 
                  add.params = list(size = 2, jitter = 0.2)) +
  stat_compare_means(label = "p.format",  method = "kruskal.test") +
  font("xlab", size = 17) +
  font("ylab", size = 17) +
  font("xy.text", size = 16) +
  font("legend.title", face = "bold") 
  
ggpar(p1, legend = "bottom", legend.title = "Plaque vulnerability index")

ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.FacetByPlaqueVulnerabilityIndex.pdf"), plot = last_plot())
compare_means(HDAC9 ~ Plaque_Vulnerability_Index, group.by = "Gender", data = AERNASE.clin.hdac9, method = "kruskal.test")
p2 <- ggpubr::ggboxplot(AERNASE.clin.hdac9, 
                  x = "Plaque_Vulnerability_Index",
                  y = "HDAC9", 
                  xlab = "Plaque vulnerability index by gender",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format",  method = "kruskal.test")
ggpar(p2, legend = "bottom", legend.title = "Plaque vulnerability index")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.PlaqueVulnerabilityIndex.byGender.pdf"), plot = last_plot())

compare_means(HDAC9 ~ Plaque_Vulnerability_Index, data = AERNASE.clin.hdac9, method = "kruskal.test")
p5 <- ggpubr::ggboxplot(AERNASE.clin.hdac9, 
                  x = "Plaque_Vulnerability_Index",
                  y = "HDAC9", 
                  xlab = "Plaque vulnerability index",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Plaque_Vulnerability_Index",
                  palette = "npg",
                  facet.by = "ORyearGroup",
                  add = "jitter") +
  stat_compare_means(label = "p.format",  method = "kruskal.test")
ggpar(p5, legend = "bottom", legend.title = "Plaque vulnerability index")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.PlaqueVulnerabilityIndex_Facet_byYear.pdf"), plot = last_plot())
compare_means(HDAC9 ~ Plaque_Vulnerability_Index, group.by = "Gender", data = AERNASE.clin.hdac9, method = "kruskal.test")
p6 <- ggpubr::ggboxplot(AERNASE.clin.hdac9, 
                  x = "Plaque_Vulnerability_Index",
                  y = "HDAC9", 
                  xlab = "Plaque vulnerability index",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  facet.by = "ORyearGroup",
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format",  method = "kruskal.test")
ggpar(p6, legend = "bottom", legend.title = "Plaque vulnerability index")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.PlaqueVulnerabilityIndex_Facet_byYear.byGender.pdf"), plot = last_plot())

Model 1

In this model we correct for Age, Gender, and year of surgery.

Here we use the inverse-rank normalized data - visually this is more normally distributed.

Analysis of the plaque vulnerability indez as a function of plaque target(s) levels.

TRAITS.TARGET.RANK.extra = c("HDAC9")

GLM.results <- data.frame(matrix(NA, ncol = 16, nrow = 0))
for (protein in 1:length(TRAITS.TARGET.RANK.extra)) {
  PROTEIN = TRAITS.TARGET.RANK.extra[protein]
  cat(paste0("\nAnalysis of ",PROTEIN,".\n"))
  TRAIT = "Plaque_Vulnerability_Index"
    cat(paste0("\n- processing ",TRAIT,"\n\n"))
    currentDF <- as.data.frame(AERNASE.clin.hdac9 %>%
      dplyr::select(., PROTEIN, TRAIT, COVARIATES_M1) %>%
      filter(complete.cases(.))) %>%
      filter_if(~is.numeric(.), all_vars(!is.infinite(.))) %>%
      droplevels(.)
    
    # fix numeric OR year
    # currentDF$ORdate_year <- as.numeric(currentDF$ORdate_year)
    
    # for debug
    # print(DT::datatable(currentDF))
    # print(nrow(currentDF))
    # print(str(currentDF))
    # print(class(currentDF[,TRAIT]))
    # table(currentDF$ORdate_year)
    ### univariate
     # + Hypertension.composite + DiabetesStatus + SmokerCurrent + 
     #            Med.Statin.LLD + Med.all.antiplatelet + GFR_MDRD + BMI + 
     #            CAD_history + Stroke_history + Peripheral.interv + stenose
    # fit <- polr(currentDF[,TRAIT] ~ currentDF[,PROTEIN] + Age + Gender + ORdate_year, 
    #           data  =  currentDF, 
    #           Hess = TRUE)
    fit <- polr(currentDF[,TRAIT] ~ currentDF[,PROTEIN] + Age + Gender + ORdate_epoch, 
              data  =  currentDF, 
              Hess = TRUE)
    print(summary(fit))
    
    ## store table
    (ctable <- coef(summary(fit)))

    ## calculate and store p values
    p <- pnorm(abs(ctable[, "t value"]), lower.tail = FALSE) * 2
    
    ## combined table
    print((ctable <- cbind(ctable, "p value" = p)))
  }

Model 2

In this model we correct for Age, Gender, Hypertension status, Diabetes status, current smoker status, lipid-lowering drugs (LLDs), antiplatelet medication, eGFR (MDRD), BMI, MedHx_CVD (combination of CAD history, stroke history, and peripheral interventions), and stenosis..


for (protein in 1:length(TRAITS.TARGET.RANK.extra)) {
  PROTEIN = TRAITS.TARGET.RANK.extra[protein]
  cat(paste0("\nAnalysis of ",PROTEIN,".\n"))
  TRAIT = "Plaque_Vulnerability_Index"
    cat(paste0("\n- processing ",TRAIT,"\n\n"))
    currentDF <- as.data.frame(AERNASE.clin.hdac9 %>%
      dplyr::select(., PROTEIN, TRAIT, COVARIATES_M2) %>%
      filter(complete.cases(.))) %>%
      filter_if(~is.numeric(.), all_vars(!is.infinite(.))) %>%
      droplevels(.)
    
    # fix numeric OR year
    # currentDF$ORdate_year <- as.numeric(currentDF$ORdate_year)
    
    # for debug
    # print(DT::datatable(currentDF))
    # print(nrow(currentDF))
    # print(str(currentDF))
    # print(class(currentDF[,TRAIT]))
    ### univariate

    # fit <- polr(as.factor(currentDF[,TRAIT]) ~ currentDF[,PROTEIN] + Age + Gender + ORdate_year + Hypertension.composite + DiabetesStatus + SmokerStatus + Med.Statin.LLD + Med.all.antiplatelet + GFR_MDRD + BMI + MedHx_CVD + stenose,
    #           data  =  currentDF,
    #           Hess = TRUE)
    
    fit <- polr(as.factor(currentDF[,TRAIT]) ~ currentDF[,PROTEIN] + Age + Gender + ORdate_epoch + Hypertension.composite + DiabetesStatus + SmokerStatus + Med.Statin.LLD + Med.all.antiplatelet + GFR_MDRD + BMI + MedHx_CVD + stenose,
              data  =  currentDF,
              Hess = TRUE)
    
    print(summary(fit))
    
    ## store table
    (ctable <- coef(summary(fit)))

    ## calculate and store p values
    p <- pnorm(abs(ctable[, "t value"]), lower.tail = FALSE) * 2
    
    ## combined table
    print((ctable <- cbind(ctable, "p value" = p)))
  }

Saving data for share

We also want to share the data with our collaborators. And provide some more graphs and summary statistics too.

summary(AERNASE.clin.hdac9$HDAC9)
ggpubr::gghistogram(AERNASE.clin.hdac9, x = "HDAC9",
                    color = "Gender", fill = "Gender",
                    add = "mean", add_density = TRUE,
                    xlab = "HDAC9 (normalized expression)",
                    palette = "npg")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Histogram.byGender.pdf"), plot = last_plot())

ggpubr::gghistogram(AERNASE.clin.hdac9, x = "HDAC9",
                    color = "Plaque_Vulnerability_Index", fill = "Plaque_Vulnerability_Index",
                    facet.by = "Plaque_Vulnerability_Index",
                    add = "mean", add_density = TRUE,
                    xlab = "HDAC9 (normalized expression)",
                    palette = "npg")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Histogram.FacetbyPVI.pdf"), plot = last_plot())

ggpubr::gghistogram(AERNASE.clin.hdac9, x = "HDAC9",
                    color = "Plaque_Vulnerability_Index", fill = "Plaque_Vulnerability_Index",
                    # facet.by = "Plaque_Vulnerability_Index",
                    add = "mean", add_density = TRUE,
                    xlab = "HDAC9 (normalized expression)",
                    palette = "npg")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Histogram.byPVI.pdf"), plot = last_plot())


ggpubr::gghistogram(AERNASE.clin.hdac9, x = "HDAC9",
                    fill = "black", rug = TRUE,
                    add = "mean", add_density = TRUE,
                    xlab = "HDAC9 (normalized expression)",
                    palette = "npg")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Histogram.pdf"), plot = last_plot())
AERNASE.clin.hdac9.forSHARE <- subset(AERNASE.clin.hdac9, select = c("STUDY_NUMBER", "Age", "Gender", "HDAC9", "Plaque_Vulnerability_Index"))
saveRDS(AERNASE.clin.hdac9.forSHARE, file = paste0(OUT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".AERNASE.clin.hdac9.forSHARE.rds"))

fwrite(AERNASE.clin.hdac9.forSHARE, file = paste0(OUT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".AERNASE.clin.hdac9.forSHARE.txt"),
       sep = "\t",
       quote = FALSE,
       na = "NA", 
       verbose = TRUE, showProgress = TRUE, nThread = 8)

Plotting HDAC9 vs Fat 10 perc. in the plaque

# Global test
compare_means(HDAC9 ~ Fat.bin_10,  data = AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_10)), method = "kruskal.test")
p1 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_10)), 
                  x = "Fat.bin_10",
                  y = "HDAC9", 
                  xlab = "Fat <10% vs >10%",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Fat.bin_10",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format",  method = "kruskal.test")
ggpar(p1, legend = "bottom", legend.title = "Fat <10% vs >10%")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Fat.bin_10.pdf"), plot = last_plot())
compare_means(HDAC9 ~ Fat.bin_10, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_10)), method = "kruskal.test")
p2 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_10)), 
                  x = "Fat.bin_10",
                  y = "HDAC9", 
                  xlab = "Fat <10% vs >10% by gender",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format",  method = "kruskal.test")
ggpar(p2, legend = "bottom", legend.title = "Fat <10% vs >10% by gender")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Fat.bin_10.byGender.pdf"), plot = last_plot())
compare_means(HDAC9 ~ Fat.bin_10, data = AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_10)), method = "kruskal.test")
p5 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_10)), 
                  x = "Fat.bin_10",
                  y = "HDAC9", 
                  xlab = "Fat <10% vs >10% by year of surgery",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Fat.bin_10",
                  palette = "npg",
                  facet.by = "ORyearGroup",
                  add = "jitter") +
  stat_compare_means(label = "p.format",  method = "kruskal.test")
ggpar(p5, legend = "bottom", legend.title = "Fat <10% vs >10% by year of surgery")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Fat.bin_10_Facet_byYear.pdf"), plot = last_plot())
compare_means(HDAC9 ~ Fat.bin_10, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_10)), method = "kruskal.test")
p6 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_10)), 
                  x = "Fat.bin_10",
                  y = "HDAC9", 
                  xlab = "Fat <10% vs >10% by year of surgery and gender",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  facet.by = "ORyearGroup",
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format",  method = "kruskal.test")
ggpar(p6, legend = "bottom", legend.title = "Fat <10% vs >10% by year of surgery and gender")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Fat.bin_10_Facet_byYear.byGender.pdf"), plot = last_plot())

Plotting HDAC9 vs Fat 40 perc. in the plaque

# Global test
compare_means(HDAC9 ~ Fat.bin_40,  data = AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_40)), method = "kruskal.test")
p1 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_40)), 
                  x = "Fat.bin_40",
                  y = "HDAC9", 
                  xlab = "Fat <40% vs >40%",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Fat.bin_40",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format",  method = "kruskal.test")
ggpar(p1, legend = "bottom", legend.title = "Fat <40% vs >40%")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Fat.bin_40.pdf"), plot = last_plot())
compare_means(HDAC9 ~ Fat.bin_40, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_40)), method = "kruskal.test")
p2 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_40)), 
                  x = "Fat.bin_40",
                  y = "HDAC9", 
                  xlab = "Fat <40% vs >40% by gender",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format",  method = "kruskal.test")
ggpar(p2, legend = "bottom", legend.title = "Fat <40% vs >40% by gender")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Fat.bin_40.byGender.pdf"), plot = last_plot())
compare_means(HDAC9 ~ Fat.bin_40, data = AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_40)), method = "kruskal.test")
p5 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_40)), 
                  x = "Fat.bin_40",
                  y = "HDAC9", 
                  xlab = "Fat <40% vs >40% by year of surgery",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Fat.bin_40",
                  palette = "npg",
                  facet.by = "ORyearGroup",
                  add = "jitter") +
  stat_compare_means(label = "p.format",  method = "kruskal.test")
ggpar(p5, legend = "bottom", legend.title = "Fat <40% vs >40% by year of surgery")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Fat.bin_40_Facet_byYear.pdf"), plot = last_plot())
compare_means(HDAC9 ~ Fat.bin_40, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_40)), method = "kruskal.test")
p6 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Fat.bin_40)), 
                  x = "Fat.bin_40",
                  y = "HDAC9", 
                  xlab = "Fat <40% vs >40% by year of surgery and gender",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  facet.by = "ORyearGroup",
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format",  method = "kruskal.test")
ggpar(p6, legend = "bottom", legend.title = "Fat <40% vs >40% by year of surgery and gender")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Fat.bin_40_Facet_byYear.byGender.pdf"), plot = last_plot())

Plotting HDAC9 vs IPH in the plaque

# Global test
compare_means(HDAC9 ~ IPH.bin,  data = AERNASE.clin.hdac9 %>% filter(!is.na(IPH.bin)), method = "kruskal.test")
p1 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(IPH.bin)), 
                  x = "IPH.bin",
                  y = "HDAC9", 
                  xlab = "Intraplaque hemorrhage (no vs. yes)",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "IPH.bin",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format",  method = "kruskal.test")
ggpar(p1, legend = "bottom", legend.title = "IPH")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.IPH.bin.pdf"), plot = last_plot())
compare_means(HDAC9 ~ IPH.bin, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(IPH.bin)), method = "kruskal.test")
p2 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(IPH.bin)), 
                  x = "IPH.bin",
                  y = "HDAC9", 
                  xlab = "Intraplaque hemorrhage (no vs. yes) by gender",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format",  method = "kruskal.test")
ggpar(p2, legend = "bottom", legend.title = "IPH by gender")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.IPH.bin.byGender.pdf"), plot = last_plot())
compare_means(HDAC9 ~ IPH.bin, data = AERNASE.clin.hdac9 %>% filter(!is.na(IPH.bin)), method = "kruskal.test")
p5 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(IPH.bin)), 
                  x = "IPH.bin",
                  y = "HDAC9", 
                  xlab = "Intraplaque hemorrhage (no vs. yes) by year of surgery",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "IPH.bin",
                  palette = "npg",
                  facet.by = "ORyearGroup",
                  add = "jitter") +
  stat_compare_means(label = "p.format",  method = "kruskal.test")
ggpar(p5, legend = "bottom", legend.title = "IPH by year of surgery")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.IPH.bin_Facet_byYear.pdf"), plot = last_plot())
compare_means(HDAC9 ~ IPH.bin, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(IPH.bin)), method = "kruskal.test")
p6 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(IPH.bin)), 
                  x = "IPH.bin",
                  y = "HDAC9", 
                  xlab = "Intraplaque hemorrhage (no vs. yes) by year of surgery and gender",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  facet.by = "ORyearGroup",
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format",  method = "kruskal.test")
ggpar(p6, legend = "bottom", legend.title = "IPH by year of surgery and gender")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.IPH.bin_Facet_byYear.byGender.pdf"), plot = last_plot())

Plotting HDAC9 vs Calcification in the plaque

# Global test
compare_means(HDAC9 ~ Calc.bin,  data = AERNASE.clin.hdac9 %>% filter(!is.na(Calc.bin)), method = "kruskal.test")
p1 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Calc.bin)), 
                  x = "Calc.bin",
                  y = "HDAC9", 
                  xlab = "Calcification (no/minor vs. moderate/heavy)",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Calc.bin",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format",  method = "kruskal.test")
ggpar(p1, legend = "bottom", legend.title = "Calcification")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Calc.bin.pdf"), plot = last_plot())
compare_means(HDAC9 ~ Calc.bin, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(Calc.bin)), method = "kruskal.test")
p2 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Calc.bin)), 
                  x = "Calc.bin",
                  y = "HDAC9", 
                  xlab = "Calcification (no/minor vs. moderate/heavy) by gender",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format",  method = "kruskal.test")
ggpar(p2, legend = "bottom", legend.title = "Calcification by gender")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Calc.bin.byGender.pdf"), plot = last_plot())
compare_means(HDAC9 ~ Calc.bin, data = AERNASE.clin.hdac9 %>% filter(!is.na(Calc.bin)), method = "kruskal.test")
p5 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Calc.bin)), 
                  x = "Calc.bin",
                  y = "HDAC9", 
                  xlab = "Calcification (no/minor vs. moderate/heavy) by year of surgery",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Calc.bin",
                  palette = "npg",
                  facet.by = "ORyearGroup",
                  add = "jitter") +
  stat_compare_means(label = "p.format",  method = "kruskal.test")
ggpar(p5, legend = "bottom", legend.title = "Calcification by year of surgery")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Calc.bin_Facet_byYear.pdf"), plot = last_plot())
compare_means(HDAC9 ~ Calc.bin, group.by = "Gender", data = AERNASE.clin.hdac9 %>% filter(!is.na(Calc.bin)), method = "kruskal.test")
p6 <- ggpubr::ggboxplot(AERNASE.clin.hdac9 %>% filter(!is.na(Calc.bin)), 
                  x = "Calc.bin",
                  y = "HDAC9", 
                  xlab = "Calcification (no/minor vs. moderate/heavy) by year of surgery and gender",
                  ylab = "HDAC9 (normalized expression)\n",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  facet.by = "ORyearGroup",
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format",  method = "kruskal.test")
ggpar(p6, legend = "bottom", legend.title = "Calcification by year of surgery and gender")
ggsave(filename = paste0(PLOT_loc, "/", Today, ".",TRAIT_OF_INTEREST,".plaque.Calc.bin_Facet_byYear.byGender.pdf"), plot = last_plot())

Session information


Version:      v1.0.5
Last update:  2023-05-31
Written by:   Sander W. van der Laan (s.w.vanderlaan-2[at]umcutrecht.nl).
Description:  Script to analyse HDAC9 from the Ather-Express Biobank Study.
Minimum requirements: R version 3.5.2 (2018-12-20) -- 'Eggshell Igloo', macOS Mojave (10.14.2).

**MoSCoW To-Do List**
The things we Must, Should, Could, and Would have given the time we have.
_M_

_S_

_C_

_W_

**Changes log**
* v1.0.5 Fixed forest plot and alternative boxplot for symptoms.
* v1.0.4 Made histogram of PVI. Exported HDAC9 and PVI data.
* v1.0.3 Small adaptations to PVI-plots.
* v1.0.2 Changed the PVI-plot.
* v1.0.1 Added figures on fat in the plaque.
* v1.0.0 Inital version.

sessionInfo()

Saving environment

save.image(paste0(PROJECT_loc, "/",Today,".",PROJECTNAME,".bulkRNAseq.additional_figures.RData"))
© 1979-2023 Sander W. van der Laan | s.w.vanderlaan[at]gmail.com | vanderlaan.science. |
LS0tCnRpdGxlOiAiQWRkaXRpb25hbCBGaWd1cmVzIgphdXRob3I6ICdbU2FuZGVyIFcuIHZhbiBkZXIgTGFhbiwgUGhEXShodHRwczovL3ZhbmRlcmxhYW4uc2NpZW5jZSkgfCBzLncudmFuZGVybGFhbkBnbWFpbC5jb20uJwpkYXRlOiAnYHIgU3lzLkRhdGUoKWAnCm91dHB1dDoKICBodG1sX25vdGVib29rOiAKICAgIGNhY2hlOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29sbGFwc2U6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBmaWcuYWxpZ246IGNlbnRlcgogICAgZmlnX2NhcHRpb246IHllcwogICAgZmlnX2hlaWdodDogMTAKICAgIGZpZ19yZXRpbmE6IDIKICAgIGZpZ193aWR0aDogMTIKICAgIHRoZW1lOiBwYXBlcgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBubwogICAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICAgIGhpZ2hsaWdodDogdGFuZ28KbWFpbmZvbnQ6IEhlbHZldGljYQpzdWJ0aXRsZTogQWNjb21wYW55aW5nICdQbGFxdWUgZXhwcmVzc2lvbiBsZXZlbHMgb2YgSERBQzkgaW4gYXNzb2NpYXRpb24gd2l0aCBwbGFxdWUgdnVsbmVyYWJpbGl0eSB0cmFpdHMgYW5kIHNlY29uZGFyeSB2YXNjdWxhciBldmVudHMgaW4gcGF0aWVudHMgdW5kZXJnb2luZyBjYXJvdGlkIGVuZGFydGVyZWN0b215LCBhbiBhbmFseXNpcyBpbiB0aGUgQXRoZXJvLUVYUFJFU1MgQmlvYmFuay4nCmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKIyBiaWJsaW9ncmFwaHk6IHJlZmVyZW5jZXMuYmliCiMga25pdDogd29yY3M6OmNpdGVfYWxsCi0tLQojIEdlbmVyYWwgU2V0dXAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIFdlIHJlY29tbWVuZCB0aGF0IHlvdSBwcmVwYXJlIHlvdXIgcmF3IGRhdGEgZm9yIGFuYWx5c2lzIGluICdwcmVwYXJlX2RhdGEuUicsCiMgYW5kIGVuZCB0aGF0IGZpbGUgd2l0aCBlaXRoZXIgb3Blbl9kYXRhKHlvdXJkYXRhKSwgb3IgY2xvc2VkX2RhdGEoeW91cmRhdGEpLgojIFRoZW4sIHVuY29tbWVudCB0aGUgbGluZSBiZWxvdyB0byBsb2FkIHRoZSBvcmlnaW5hbCBvciBzeW50aGV0aWMgZGF0YQojICh3aGljaGV2ZXIgaXMgYXZhaWxhYmxlKSwgdG8gYWxsb3cgYW55b25lIHRvIHJlcHJvZHVjZSB5b3VyIGNvZGU6CiMgbG9hZF9kYXRhKCkKCiMgZnVydGhlciBkZWZpbmUgc29tZSBrbml0ci1vcHRpb25zLgprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4LCBmaWcucGF0aCA9ICdGaWd1cmVzLycsIAogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IFRSVUUsICMgc2hvdyB3YXJuaW5ncyBkdXJpbmcgY29kZWJvb2sgZ2VuZXJhdGlvbgogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IFRSVUUsICMgc2hvdyBtZXNzYWdlcyBkdXJpbmcgY29kZWJvb2sgZ2VuZXJhdGlvbgogICAgICAgICAgICAgICAgICAgICAgZXJyb3IgPSBUUlVFLCAjIGRvIG5vdCBpbnRlcnJ1cHQgY29kZWJvb2sgZ2VuZXJhdGlvbiBpbiBjYXNlIG9mIGVycm9ycywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdXN1YWxseSBiZXR0ZXIgZm9yIGRlYnVnZ2luZwogICAgICAgICAgICAgICAgICAgICAgZWNobyA9IFRSVUUsICAjIHNob3cgUiBjb2RlCiAgICAgICAgICAgICAgICAgICAgICBldmFsID0gVFJVRSkKCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9taW5pbWFsKCkpCiMgcGFuZGVyOjpwYW5kZXJPcHRpb25zKCJ0YWJsZS5zcGxpdC50YWJsZSIsIEluZikKbGlicmFyeSgid29yY3MiKQpsaWJyYXJ5KCJybWFya2Rvd24iKQoKYGBgCgpgYGB7ciBlY2hvID0gRkFMU0V9CnJtKGxpc3QgPSBscygpKQpgYGAKCmBgYHtyIExvY2FsU3lzdGVtLCBlY2hvID0gRkFMU0V9CiMjIyBPcGVyYXRpbmcgU3lzdGVtIFZlcnNpb24KIyMjIE1hY0Jvb2sgUHJvClJPT1RfbG9jID0gIi9Vc2Vycy9zbGFhbjMiCgojIyMgR2VuZXJhbApHRU5PTUlDX2xvYyA9IHBhc3RlMChST09UX2xvYywgIi9PbmVEcml2ZSAtIFVNQyBVdHJlY2h0L0dlbm9taWNzIikKQUVEQl9sb2MgPSBwYXN0ZTAoR0VOT01JQ19sb2MsICIvQXRoZXJvLUV4cHJlc3MvQUUtQUFBX0dTX0RCcyIpCkxBQl9sb2MgPSBwYXN0ZTAoR0VOT01JQ19sb2MsICIvTGFiQnVzaW5lc3MiKQoKUFJPSkVDVF9sb2MgPSBwYXN0ZTAoUk9PVF9sb2MsICIvZ2l0L0NpcmN1bGF0b3J5SGVhbHRoL0FFXzIwMjExMjAxX1lBV19TV1ZBTkRFUkxBQU5fSERBQzkiKQoKIyBHZW5ldGljIGFuZCBnZW5vbWljIGRhdGEKU1RPUkFHRV9sb2MgPSBwYXN0ZTAoUk9PVF9sb2MsICIvUExJTksiKQpBRVJOQV9sb2MgPSBwYXN0ZTAoU1RPUkFHRV9sb2MsICIvX0FFX09SSUdJTkFMUy9BRVJOQSIpCkFFU0NSTkFfbG9jID0gcGFzdGUwKFNUT1JBR0VfbG9jLCAiL19BRV9PUklHSU5BTFMvQUVTQ1JOQS9wcmVwcGVkX2RhdGEiKQpBRUdTUUNfbG9jID0gcGFzdGUwKFNUT1JBR0VfbG9jLCAiL19BRV9PUklHSU5BTFMvQUVHU19DT01CSU5FRF9RQzIwMTgiKQoKIyMjIFNPTUUgVkFSSUFCTEVTIFdFIE5FRUQgRE9XTiBUSEUgTElORQpUUkFJVF9PRl9JTlRFUkVTVCA9ICJIREFDOSIgIyBQaGVub3R5cGUKUFJPSkVDVE5BTUUgPSAiSERBQzkiCgpjYXQoIlxuQ3JlYXRlIGEgbmV3IGFuYWx5c2lzIGRpcmVjdG9yeS4uLlxuIikKaWZlbHNlKCFkaXIuZXhpc3RzKGZpbGUucGF0aChQUk9KRUNUX2xvYywgIi8iLFBST0pFQ1ROQU1FKSksIAogICAgICAgZGlyLmNyZWF0ZShmaWxlLnBhdGgoUFJPSkVDVF9sb2MsICIvIixQUk9KRUNUTkFNRSkpLCAKICAgICAgIEZBTFNFKQpBTkFMWVNJU19sb2MgPSBwYXN0ZTAoUFJPSkVDVF9sb2MsIi8iLFBST0pFQ1ROQU1FKQoKaWZlbHNlKCFkaXIuZXhpc3RzKGZpbGUucGF0aChBTkFMWVNJU19sb2MsICIvUExPVFMiKSksIAogICAgICAgZGlyLmNyZWF0ZShmaWxlLnBhdGgoQU5BTFlTSVNfbG9jLCAiL1BMT1RTIikpLCAKICAgICAgIEZBTFNFKQpQTE9UX2xvYyA9IHBhc3RlMChBTkFMWVNJU19sb2MsIi9QTE9UUyIpCgppZmVsc2UoIWRpci5leGlzdHMoZmlsZS5wYXRoKFBMT1RfbG9jLCAiL1FDIikpLCAKICAgICAgIGRpci5jcmVhdGUoZmlsZS5wYXRoKFBMT1RfbG9jLCAiL1FDIikpLCAKICAgICAgIEZBTFNFKQpRQ19sb2MgPSBwYXN0ZTAoUExPVF9sb2MsIi9RQyIpCgppZmVsc2UoIWRpci5leGlzdHMoZmlsZS5wYXRoKEFOQUxZU0lTX2xvYywgIi9PVVRQVVQiKSksIAogICAgICAgZGlyLmNyZWF0ZShmaWxlLnBhdGgoQU5BTFlTSVNfbG9jLCAiL09VVFBVVCIpKSwgCiAgICAgICBGQUxTRSkKT1VUX2xvYyA9IHBhc3RlMChBTkFMWVNJU19sb2MsICIvT1VUUFVUIikKCmlmZWxzZSghZGlyLmV4aXN0cyhmaWxlLnBhdGgoQU5BTFlTSVNfbG9jLCAiL0JBU0VMSU5FIikpLCAKICAgICAgIGRpci5jcmVhdGUoZmlsZS5wYXRoKEFOQUxZU0lTX2xvYywgIi9CQVNFTElORSIpKSwgCiAgICAgICBGQUxTRSkKQkFTRUxJTkVfbG9jID0gcGFzdGUwKEFOQUxZU0lTX2xvYywgIi9CQVNFTElORSIpCgppZmVsc2UoIWRpci5leGlzdHMoZmlsZS5wYXRoKEFOQUxZU0lTX2xvYywgIi9DT1giKSksIAogICAgICAgZGlyLmNyZWF0ZShmaWxlLnBhdGgoQU5BTFlTSVNfbG9jLCAiL0NPWCIpKSwgCiAgICAgICBGQUxTRSkKQ09YX2xvYyA9IHBhc3RlMChBTkFMWVNJU19sb2MsICIvQ09YIikKCgoKc2V0d2QocGFzdGUwKFBST0pFQ1RfbG9jKSkKZ2V0d2QoKQpsaXN0LmZpbGVzKCkKCmBgYAoKYGBge3IgU291cmNlIGZ1bmN0aW9uc30Kc291cmNlKHBhc3RlMChQUk9KRUNUX2xvYywgIi9zY3JpcHRzL2Z1bmN0aW9ucy5SIikpCmBgYAoKYGBge3IgU2V0dGluZzogbG9hZGluZ19wYWNrYWdlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcy5hdXRvKCJyZWFkciIpCmluc3RhbGwucGFja2FnZXMuYXV0bygib3B0cGFyc2UiKQppbnN0YWxsLnBhY2thZ2VzLmF1dG8oInRvb2xzIikKaW5zdGFsbC5wYWNrYWdlcy5hdXRvKCJkcGx5ciIpCmluc3RhbGwucGFja2FnZXMuYXV0bygidGlkeXIiKQppbnN0YWxsLnBhY2thZ2VzLmF1dG8oIm5hbmlhciIpCgojIFRvIGdldCAnZGF0YS50YWJsZScgd2l0aCAnZndyaXRlJyB0byBiZSBhYmxlIHRvIGRpcmVjdGx5IHdyaXRlIGd6aXBwZWQtZmlsZXMKIyBSZWY6IGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQyNzg4NDAxL2lzLXBvc3NpYmxlLXRvLXVzZS1md3JpdGUtZnJvbS1kYXRhLXRhYmxlLXdpdGgtZ3pmaWxlCiMgaW5zdGFsbC5wYWNrYWdlcygiZGF0YS50YWJsZSIsIHJlcG9zID0gImh0dHBzOi8vUmRhdGF0YWJsZS5naXRsYWIuaW8vZGF0YS50YWJsZSIpCmxpYnJhcnkoZGF0YS50YWJsZSkKCmluc3RhbGwucGFja2FnZXMuYXV0bygidGlkeXZlcnNlIikKaW5zdGFsbC5wYWNrYWdlcy5hdXRvKCJrbml0ciIpCmluc3RhbGwucGFja2FnZXMuYXV0bygiRFQiKQppbnN0YWxsLnBhY2thZ2VzLmF1dG8oIk1BU1MiKQojIGluc3RhbGwucGFja2FnZXMuYXV0bygiU2V1cmF0IikgIyBsYXRlc3QgdmVyc2lvbgoKIyBJbnN0YWxsIHRoZSBkZXZ0b29scyBwYWNrYWdlIGZyb20gSGFkbGV5IFdpY2toYW0KaW5zdGFsbC5wYWNrYWdlcy5hdXRvKCdkZXZ0b29scycpCgppbnN0YWxsLnBhY2thZ2VzLmF1dG8oImhhdmVuIikKaW5zdGFsbC5wYWNrYWdlcy5hdXRvKCJzamxhYmVsbGVkIikKaW5zdGFsbC5wYWNrYWdlcy5hdXRvKCJzalBsb3QiKQppbnN0YWxsLnBhY2thZ2VzLmF1dG8oImxhYmVsbGVkIikKaW5zdGFsbC5wYWNrYWdlcy5hdXRvKCJ0YWJsZW9uZSIpCgppbnN0YWxsLnBhY2thZ2VzLmF1dG8oImdncHViciIpCgpgYGAKCmBgYHtyIFNldHRpbmc6IENvbG9ycywgaW5jbHVkZSA9IEZBTFNFfQoKVG9kYXkgPSBmb3JtYXQoYXMuRGF0ZShhcy5QT1NJWGx0KFN5cy50aW1lKCkpKSwgIiVZJW0lZCIpClRvZGF5LlJlcG9ydCA9IGZvcm1hdChhcy5EYXRlKGFzLlBPU0lYbHQoU3lzLnRpbWUoKSkpLCAiJUEsICVCICVkLCAlWSIpCgojIyMgVXRyZWNodFNjaWVuY2VQYXJrQ29sb3Vyc1NjaGVtZQojIyMKIyMjIFdlYnNpdGV0b2NvbnZlcnRIRVh0b1JHQjpodHRwOi8vaGV4LmNvbG9ycnJzLmNvbS4KIyMjIEZvcnNvbWVmdW5jdGlvbnN5b3VzaG91bGRkaXZpZGV0aGVzZW51bWJlcnNieTI1NS4KIyMjIAojIyMJTm8uCUNvbG9yCQkJICAgICAgSEVYCShSR0IpCQkJCQkJICAgICAgICAgICAgICBDSFIJCSAgTUFGL0lORk8KIyMjLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIwkxCSAgeWVsbG93CQkJICAgICNGQkI4MjAgKDI1MSwxODQsMzIpCQkJCSAgICAgID0+CTEJCW9yIDEuMD5JTkZPCiMjIwkyCSAgZ29sZAkJCSAgICAgICNGNTlEMTAgKDI0NSwxNTcsMTYpCQkJCSAgICAgID0+CTIJCQojIyMJMwkgIHNhbG1vbgkJCSAgICAjRTU1NzM4ICgyMjksODcsNTYpCQkJCSAgICAgID0+CTMJCW9yIDAuMDU8TUFGPDAuMiBvciAwLjQ8SU5GTzwwLjYKIyMjCTQJICBkYXJrcGluawkJICAgICNEQjAwM0YgKCgyMTksMCw2MykJCQkJICAgICAgPT4JNAkJCiMjIwk1CSAgbGlnaHRwaW5rCQkgICAgI0UzNTQ5MyAoMjI3LDg0LDE0NykJCQkJICAgICAgPT4JNQkJb3IgMC44PElORk88MS4wCiMjIwk2CSAgcGluawkJCSAgICAgICNENTI2N0IgKDIxMywzOCwxMjMpCQkJCSAgICAgID0+CTYJCQojIyMJNwkgIGhhcmRwaW5rCQkgICAgI0NDMDA3MSAoMjA0LDAsMTEzKQkJCQkgICAgICA9Pgk3CQkKIyMjCTgJICBsaWdodHB1cnBsZQkgICAgI0E4NDQ4QSAoMTY4LDY4LDEzOCkJCQkJICAgICAgPT4JOAkJCiMjIwk5CSAgcHVycGxlCQkJICAgICM5QTM0ODAgKDE1NCw1MiwxMjgpCQkJCSAgICAgID0+CTkJCQojIyMJMTAJbGF2ZW5kZWwJCSAgICAjOEQ1QjlBICgxNDEsOTEsMTU0KQkJCQkgICAgICA9PgkxMAkJCiMjIwkxMQlibHVlcHVycGxlCQkgICM3MDUyOTYgKDExMiw4MiwxNTApCQkJCSAgICAgID0+CTExCQkKIyMjCTEyCXB1cnBsZWJsdWUJCSAgIzY4NkFBOSAoMTA0LDEwNiwxNjkpCQkJICAgICAgPT4JMTIJCQojIyMJMTMJbGlnaHRwdXJwbGVibHVlCSM2MTczQUQgKDk3LDExNSwxNzMvMTAxLDEyMCwxODApCT0+CTEzCQkKIyMjCTE0CXNlYWJsdWUJCQkgICAgIzRDODFCRiAoNzYsMTI5LDE5MSkJCQkJICAgICAgPT4JMTQJCQojIyMJMTUJc2t5Ymx1ZQkJCSAgICAjMkY4QkM5ICg0NywxMzksMjAxKQkJCQkgICAgICA9PgkxNQkJCiMjIwkxNglhenVyYmx1ZQkJICAgICMxMjkwRDkgKDE4LDE0NCwyMTcpCQkJCSAgICAgID0+CTE2CQlvciAwLjAxPE1BRjwwLjA1IG9yIDAuMjxJTkZPPDAuNAojIyMJMTcJbGlnaHRhenVyYmx1ZQkgICMxMzk2RDggKDE5LDE1MCwyMTYpCQkJCSAgICAgID0+CTE3CQkKIyMjCTE4CWdyZWVuYmx1ZQkJICAgICMxNUE2QzEgKDIxLDE2NiwxOTMpCQkJCSAgICAgID0+CTE4CQkKIyMjCTE5CXNlYXdlZWRncmVlbgkgICM1RUIxN0YgKDk0LDE3NywxMjcpCQkJCSAgICAgID0+CTE5CQkKIyMjCTIwCXllbGxvd2dyZWVuCQkgICM4NkI4MzMgKDEzNCwxODQsNTEpCQkJCSAgICAgID0+CTIwCQkKIyMjCTIxCWxpZ2h0bW9zc2dyZWVuCSNDNUQyMjAgKDE5NywyMTAsMzIpCQkJCSAgICAgID0+CTIxCQkKIyMjCTIyCW1vc3NncmVlbgkJICAgICM5RkMyMjggKDE1OSwxOTQsNDApCQkJCSAgICAgID0+CTIyCQlvciBNQUY+MC4yMCBvciAwLjY8SU5GTzwwLjgKIyMjCTIzCWxpZ2h0Z3JlZW4JICAJIzc4QjExMyAoMTIwLDE3NywxOSkJCQkJICAgICAgPT4JMjMvWAojIyMJMjQJZ3JlZW4JCQkgICAgICAjNDlBMDFEICg3MywxNjAsMjkpCQkJCSAgICAgID0+CTI0L1kKIyMjCTI1CWdyZXkJCQkgICAgICAjNTk1QTVDICg4OSw5MCw5MikJCQkJICAgICAgICA9PgkyNS9YWQlvciBNQUY8MC4wMSBvciAwLjA8SU5GTzwwLjIKIyMjCTI2CWxpZ2h0Z3JleQkJICAgICNBMkEzQTQJKDE2MiwxNjMsMTY0KQkJCSAgICAgID0+CTI2L01UCiMjIwojIyMJQURESVRJT05BTCBDT0xPUlMKIyMjCTI3CW1pZGdyZXkJCQkjRDdEOEQ3CiMjIwkyOAl2ZXJ5bGlnaHRncmV5CSNFQ0VDRUMiCiMjIwkyOQl3aGl0ZQkJCSNGRkZGRkYKIyMjCTMwCWJsYWNrCQkJIzAwMDAwMAojIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgp1aXRob2ZfY29sb3IgPSBjKCIjRkJCODIwIiwiI0Y1OUQxMCIsIiNFNTU3MzgiLCIjREIwMDNGIiwiI0UzNTQ5MyIsIiNENTI2N0IiLAogICAgICAgICAgICAgICAgICIjQ0MwMDcxIiwiI0E4NDQ4QSIsIiM5QTM0ODAiLCIjOEQ1QjlBIiwiIzcwNTI5NiIsIiM2ODZBQTkiLAogICAgICAgICAgICAgICAgICIjNjE3M0FEIiwiIzRDODFCRiIsIiMyRjhCQzkiLCIjMTI5MEQ5IiwiIzEzOTZEOCIsIiMxNUE2QzEiLAogICAgICAgICAgICAgICAgICIjNUVCMTdGIiwiIzg2QjgzMyIsIiNDNUQyMjAiLCIjOUZDMjI4IiwiIzc4QjExMyIsIiM0OUEwMUQiLAogICAgICAgICAgICAgICAgICIjNTk1QTVDIiwiI0EyQTNBNCIsICIjRDdEOEQ3IiwgIiNFQ0VDRUMiLCAiI0ZGRkZGRiIsICIjMDAwMDAwIikKCnVpdGhvZl9jb2xvcl9sZWdlbmQgPSBjKCIjRkJCODIwIiwgIiNGNTlEMTAiLCAiI0U1NTczOCIsICIjREIwMDNGIiwgIiNFMzU0OTMiLAogICAgICAgICAgICAgICAgICAgICAgICAiI0Q1MjY3QiIsICIjQ0MwMDcxIiwgIiNBODQ0OEEiLCAiIzlBMzQ4MCIsICIjOEQ1QjlBIiwKICAgICAgICAgICAgICAgICAgICAgICAgIiM3MDUyOTYiLCAiIzY4NkFBOSIsICIjNjE3M0FEIiwgIiM0QzgxQkYiLCAiIzJGOEJDOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICIjMTI5MEQ5IiwgIiMxMzk2RDgiLCAiIzE1QTZDMSIsICIjNUVCMTdGIiwgIiM4NkI4MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAiI0M1RDIyMCIsICIjOUZDMjI4IiwgIiM3OEIxMTMiLCAiIzQ5QTAxRCIsICIjNTk1QTVDIiwKICAgICAgICAgICAgICAgICAgICAgICAgIiNBMkEzQTQiLCAiI0Q3RDhENyIsICIjRUNFQ0VDIiwgIiNGRkZGRkYiLCAiIzAwMDAwMCIpCgoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBgCgoKIyBCYWNrZ3JvdW5kCgpUaGlzIG5vdGVib29rIGNvbnRhaW5zIGFkZGl0aW9uYWwgZmlndXJlcyBvZiB0aGUgcHJvamVjdCAiUGxhcXVlIGV4cHJlc3Npb24gbGV2ZWxzIG9mIF9IREFDOV8gaW4gYXNzb2NpYXRpb24gd2l0aCBwbGFxdWUgdnVsbmVyYWJpbGl0eSB0cmFpdHMgYW5kIHNlY29uZGFyeSB2YXNjdWxhciBldmVudHMgaW4gcGF0aWVudHMgdW5kZXJnb2luZyBjYXJvdGlkIGVuZGFydGVyZWN0b215OiBhbiBhbmFseXNpcyBpbiB0aGUgQXRoZXJvLUVYUFJFU1MgQmlvYmFuay4iLgoKCiMgTG9hZGluZyBkYXRhCgpgYGB7ciBMb2FkaW5nIHByb2plY3QgZGF0YX0KIyBsb2FkKHBhc3RlMChQUk9KRUNUX2xvYywgIi8iLFRvZGF5LCIuIixQUk9KRUNUTkFNRSwiLmJ1bGtSTkFzZXEubWFpbl9hbmFseXNpcy5SRGF0YSIpKQpsb2FkKHBhc3RlMChQUk9KRUNUX2xvYywgIi8yMDIyMDMxOS4iLFBST0pFQ1ROQU1FLCIuYnVsa1JOQXNlcS5tYWluX2FuYWx5c2lzLlJEYXRhIikpCgpgYGAKCgojIEZpeCBzb21lIHZhcmlhYmxlcwoKV2UgbmVlZCB0byBnZXQgdGhlICdjb252ZW50aW9uYWwgdW5pdCcgdmVyc2lvbnMgb2YgY2hvbGVzdGVyb2xzLgoKYGBge3J9CkFFUk5BU0UuY2xpbi5oZGFjOSA8LSBtZXJnZShBRVJOQVNFLmNsaW4uaGRhYzksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0KEFFREIuQ0VBLCBzZWxlY3QgPSBjKCJTVFVEWV9OVU1CRVIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicmlzazYxNCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMRExfZmluYWxDVSIsICJIRExfZmluYWxDVSIsICJUQ19maW5hbENVIiwgIlRHX2ZpbmFsQ1UiKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkueCA9ICJTVFVEWV9OVU1CRVIiLCBieS55ID0gIlNUVURZX05VTUJFUiIsIHNvcnQgPSBUUlVFLCBhbGwueCA9IFRSVUUpCmBgYAoKCiMgQWRkaXRpb25hbCBmaWd1cmVzCgojIyBBZ2UgYW5kIHNleApXZSB3YW50IHRvIGNyZWF0ZSBwZXItYWdlLWdyb3VwIGZpZ3VyZXMgbWVkaWFuIMKxIGludGVycXVhcnRpbGUgcmFuZ2UuIAoKLSBCb3ggYW5kIFdoaXNrZXIgcGxvdCBmb3IgdGFyZ2V0KHMpIHBsYXF1ZSBsZXZlbHMgYnkgc2V4LgotIEJveCBhbmQgV2hpc2tlciBwbG90IGZvciB0YXJnZXQocykgcGxhcXVlIGxldmVscyBieSAoc2V4IGFuZCkgYWdlIGdyb3VwICg8NTUsIDU1LTY0LCA2NS03NCwgNzUtODQsIDg1KykuCgoKYGBge3IgcGVyIFNleH0KCiMgP2dncHVicjo6Z2dib3hwbG90KCkKY29tcGFyZV9tZWFucyhIREFDOSB+IEdlbmRlciwgIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzksIAogICAgICAgICAgICAgICAgICB4ID0gYygiR2VuZGVyIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuR2VuZGVyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgoKYGBgCgpgYGB7ciBBZ2VHcm91cHN9CmxpYnJhcnkoZHBseXIpCgpBRVJOQVNFLmNsaW4uaGRhYzkgPC0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBkcGx5cjo6bXV0YXRlKEFnZUdyb3VwID0gZmFjdG9yKGNhc2Vfd2hlbihBZ2UgPCA1NSB+ICI8NTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFnZSA+PSA1NSAgJiBBZ2UgPD0gNjQgfiAiNTUtNjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFnZSA+PSA2NSAgJiBBZ2UgPD0gNzQgfiAiNjUtNzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFnZSA+PSA3NSAgJiBBZ2UgPD0gODQgfiAiNzUtODQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFnZSA+PSA4NSB+ICI4NSsiKSkpIAoKQUVSTkFTRS5jbGluLmhkYWM5IDwtIEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZHBseXI6Om11dGF0ZShBZ2VHcm91cFNleCA9IGZhY3RvcihjYXNlX3doZW4oQWdlIDwgNTUgJiBHZW5kZXIgPT0gIm1hbGUiIH4gIjw1NSBtYWxlcyIgLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFnZSA+PSA1NSAgJiBBZ2UgPD0gNjQgJiBHZW5kZXIgPT0gIm1hbGUifiAiNTUtNjQgbWFsZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFnZSA+PSA2NSAgJiBBZ2UgPD0gNzQgJiBHZW5kZXIgPT0gIm1hbGUifiAiNjUtNzQgbWFsZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFnZSA+PSA3NSAgJiBBZ2UgPD0gODQgJiBHZW5kZXIgPT0gIm1hbGUifiAiNzUtODQgbWFsZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFnZSA+PSA4NSAmIEdlbmRlciA9PSAibWFsZSJ+ICI4NSsgbWFsZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFnZSA8IDU1ICYgR2VuZGVyID09ICJmZW1hbGUiIH4gIjw1NSBmZW1hbGVzIiAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlID49IDU1ICAmIEFnZSA8PSA2NCAmIEdlbmRlciA9PSAiZmVtYWxlIn4gIjU1LTY0IGZlbWFsZXMgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBZ2UgPj0gNjUgICYgQWdlIDw9IDc0ICYgR2VuZGVyID09ICJmZW1hbGUifiAiNjUtNzQgZmVtYWxlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlID49IDc1ICAmIEFnZSA8PSA4NCAmIEdlbmRlciA9PSAiZmVtYWxlIn4gIjc1LTg0IGZlbWFsZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFnZSA+PSA4NSAmIEdlbmRlciA9PSAiZmVtYWxlIn4gIjg1KyBmZW1hbGVzIikpKQoKdGFibGUoQUVSTkFTRS5jbGluLmhkYWM5JEFnZUdyb3VwLCBBRVJOQVNFLmNsaW4uaGRhYzkkR2VuZGVyKQp0YWJsZShBRVJOQVNFLmNsaW4uaGRhYzkkQWdlR3JvdXBTZXgpCgpgYGAKCk5vdyB3ZSBjYW4gZHJhdyBzb21lIGdyYXBocyBvZiBwbGFxdWUgdGFyZ2V0KHMpIGxldmVscyBwZXIgc2V4IGFuZCBhZ2UgZ3JvdXAgYXMgbWVkaWFuIMKxIGludGVycXVhcnRpbGUgcmFuZ2UuCgpgYGB7ciBwZXIgQWdlR3JvdXAgcGVyIFNleH0KCiMgP2dncHVicjo6Z2dib3hwbG90KCkKY29tcGFyZV9tZWFucyhIREFDOSB+IEFnZUdyb3VwLCAgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJBZ2VHcm91cCIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiQWdlIGdyb3VwcyAoeWVhcnMpIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkFnZUdyb3VwIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICAjIGFkZCA9ICJtZWRpYW5faXFyIikKICAgICAgICAgICAgICAgICAgYWRkID0gYygibWVkaWFuX2lxciIsICJqaXR0ZXIiKSkgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBBZ2VHcm91cCksIGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uaGRhYzkuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5BZ2VHcm91cC5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKY29tcGFyZV9tZWFucyhIREFDOSB+IEFnZUdyb3VwLCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5LCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5LCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIkFnZUdyb3VwIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJBZ2UgZ3JvdXBzICh5ZWFycykgcGVyIGdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbiIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgIyBhZGQgPSAibWVkaWFuX2lxciIpCiAgICAgICAgICAgICAgICAgIGFkZCA9IGMoIm1lZGlhbl9pcXIiLCAiaml0dGVyIikpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gR2VuZGVyKSwgbGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkFnZUdyb3VwX3BlckdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQpgYGAKCgojIyBIeXBlcnRlbnNpb24gJiBibG9vZCBwcmVzc3VyZQpXZSB3YW50IHRvIGNyZWF0ZSBmaWd1cmVzIG9mIHRhcmdldChzKSBsZXZlbHMgc3RyYXRpZmllZCBieSBoeXBlcnRlbnNpb24vYmxvb2QgcHJlc3N1cmUsIGFuZCB1c2Ugb2YgYW50aS1oeXBlcnRlbnNpdmUgZHJ1Z3MuIAoKLSBCb3ggYW5kIFdoaXNrZXIgcGxvdCBmb3IgdGFyZ2V0KHMpIHBsYXF1ZSBsZXZlbHMgYnkgaHlwZXJ0ZW5zaW9uIGdyb3VwIChubywgeWVzKQotIEJveCBhbmQgV2hpc2tlciBwbG90IGZvciB0YXJnZXQocykgcGxhcXVlIGxldmVscyBieSBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSBncm91cCAoPDEyMCwgMTIwLTEzOSwgMTQwLTE1OSwxNjArKQoKCmBgYHtyIEJsb29kUHJlc3N1cmV9CmxpYnJhcnkoZHBseXIpCgpBRVJOQVNFLmNsaW4uaGRhYzkgPC0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBtdXRhdGUoU0JQR3JvdXAgPSBmYWN0b3IoY2FzZV93aGVuKHN5c3RvbGljIDwgMTIwIH4gIjwxMjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN5c3RvbGljID49IDEyMCAgJiBzeXN0b2xpYyA8PSAxMzkgfiAiMTIwLTEzOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3lzdG9saWMgPj0gMTQwICAmIHN5c3RvbGljIDw9IDE1OSB+ICIxNDAtMTU5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzeXN0b2xpYyA+PSAxNjAgfiAiMTYwKyIpKSkgCgp0YWJsZShBRVJOQVNFLmNsaW4uaGRhYzkkU0JQR3JvdXAsIEFFUk5BU0UuY2xpbi5oZGFjOSRHZW5kZXIpCgpgYGAKCk5vdyB3ZSBjYW4gZHJhdyBzb21lIGdyYXBocyBvZiBwbGFxdWUgdGFyZ2V0KHMpIGxldmVscyBwZXIgc2V4IGFuZCBoeXBlcnRlbnNpb24vYmxvb2QgcHJlc3N1cmUgZ3JvdXAgYXMgbWVkaWFuIMKxIGludGVycXVhcnRpbGUgcmFuZ2UuCgpgYGB7ciBwZXIgQmxvb2RQcmVzc3VyZSBwZXIgU2V4fQpjb21wYXJlX21lYW5zKEhEQUM5IH4gU0JQR3JvdXAsIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoU0JQR3JvdXApKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShTQlBHcm91cCkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIlNCUEdyb3VwIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJTeXN0b2xpYyBibG9vZCBwcmVzc3VyZSAobW1IZykiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiU0JQR3JvdXAiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IFNCUEdyb3VwKSwgbGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLlNCUEdyb3VwLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhEQUM5IH4gSHlwZXJ0ZW5zaW9uLnNlbGZyZXBvcnQsIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoSHlwZXJ0ZW5zaW9uLnNlbGZyZXBvcnQpKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShIeXBlcnRlbnNpb24uc2VsZnJlcG9ydCkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIkh5cGVydGVuc2lvbi5zZWxmcmVwb3J0IiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJTZWxmLXJlcG9ydGVkIGh5cGVydGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJIeXBlcnRlbnNpb24uc2VsZnJlcG9ydCIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gSHlwZXJ0ZW5zaW9uLnNlbGZyZXBvcnQpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuSHlwZXJ0ZW5zaW9uLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhEQUM5IH4gSHlwZXJ0ZW5zaW9uLmRydWdzLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKEh5cGVydGVuc2lvbi5kcnVncykpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKEh5cGVydGVuc2lvbi5kcnVncykpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIkh5cGVydGVuc2lvbi5kcnVncyIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiSHlwZXJ0ZW5zaW9uIG1lZGljYXRpb24gdXNlIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkh5cGVydGVuc2lvbi5kcnVncyIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gSHlwZXJ0ZW5zaW9uLmRydWdzKSwgbGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkh5cGVydGVuc2lvbkRydWdzLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgoKCmNvbXBhcmVfbWVhbnMoSERBQzkgfiBTQlBHcm91cCwgZ3JvdXAuYnkgPSAiR2VuZGVyIiwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShTQlBHcm91cCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKFNCUEdyb3VwKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiU0JQR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIlN5c3RvbGljIGJsb29kIHByZXNzdXJlIChtbUhnKSBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gR2VuZGVyKSwgbGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLlNCUEdyb3VwX2J5R2VuZGVyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhEQUM5IH4gSHlwZXJ0ZW5zaW9uLnNlbGZyZXBvcnQsIGdyb3VwLmJ5ID0gIkdlbmRlciIsIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoSHlwZXJ0ZW5zaW9uLnNlbGZyZXBvcnQpKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShIeXBlcnRlbnNpb24uc2VsZnJlcG9ydCkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIkh5cGVydGVuc2lvbi5zZWxmcmVwb3J0IiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJTZWxmLXJlcG9ydGVkIGh5cGVydGVuc2lvbiBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gR2VuZGVyKSwgbGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkh5cGVydGVuc2lvbl9ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKY29tcGFyZV9tZWFucyhIREFDOSB+IEh5cGVydGVuc2lvbi5kcnVncywgZ3JvdXAuYnkgPSAiR2VuZGVyIiwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShIeXBlcnRlbnNpb24uZHJ1Z3MpKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShIeXBlcnRlbnNpb24uZHJ1Z3MpKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJIeXBlcnRlbnNpb24uZHJ1Z3MiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkh5cGVydGVuc2lvbiBtZWRpY2F0aW9uIHVzZSBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gR2VuZGVyKSwgbGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkh5cGVydGVuc2lvbi5kcnVnc19ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKCgpjb21wYXJlX21lYW5zKEhEQUM5IH4gU0JQR3JvdXAsIGdyb3VwLmJ5ID0gIkh5cGVydGVuc2lvbi5kcnVncyIsIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoU0JQR3JvdXApICYgIWlzLm5hKEh5cGVydGVuc2lvbi5kcnVncykpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKFNCUEdyb3VwKSAmICFpcy5uYShIeXBlcnRlbnNpb24uZHJ1Z3MpKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJTQlBHcm91cCIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiU3lzdG9saWMgYmxvb2QgcHJlc3N1cmUgKG1tSGcpIGJ5IG1lZGljYXRpb24gdXNlIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkh5cGVydGVuc2lvbi5kcnVncyIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjNDlBMDFEIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gSHlwZXJ0ZW5zaW9uLmRydWdzKSwgbGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLlNCUEdyb3VwX2J5SHlwZXJ0ZW5zaW9uRHJ1Z3MucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSERBQzkgfiBIeXBlcnRlbnNpb24uc2VsZnJlcG9ydCwgZ3JvdXAuYnkgPSAiSHlwZXJ0ZW5zaW9uLmRydWdzIiwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShIeXBlcnRlbnNpb24uc2VsZnJlcG9ydCkgJiAhaXMubmEoSHlwZXJ0ZW5zaW9uLmRydWdzKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoSHlwZXJ0ZW5zaW9uLnNlbGZyZXBvcnQpICYgIWlzLm5hKEh5cGVydGVuc2lvbi5kcnVncykpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIkh5cGVydGVuc2lvbi5zZWxmcmVwb3J0IiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJTZWxmLXJlcG9ydGVkIGh5cGVydGVuc2lvbiBwZXIgbWVkaWNhdGlvbiB1c2UiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiSHlwZXJ0ZW5zaW9uLmRydWdzIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiM0OUEwMUQiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBIeXBlcnRlbnNpb24uZHJ1Z3MpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuSHlwZXJ0ZW5zaW9uLnNlbGZyZXBvcnRfYnlIeXBlcnRlbnNpb25EcnVncy5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKYGBgCgoKIyMgSHlwZXJjaG9sZXN0ZXJvbGVtaWEgJiBMREwgbGV2ZWxzCldlIHdhbnQgdG8gY3JlYXRlIGZpZ3VyZXMgb2YgdGFyZ2V0KHMpIGxldmVscyBzdHJhdGlmaWVkIGJ5IGh5cGVyY2hvbGVzdGVyb2xlbWlhL0xETC1sZXZlbHMsIGFuZCB1c2Ugb2YgbGlwaWQtbG93ZXJpbmcgZHJ1Z3MuIAoKLSBCb3ggYW5kIFdoaXNrZXIgcGxvdCBmb3IgdGFyZ2V0KHMpIHBsYXF1ZSBsZXZlbHMgYnkgaHlwZXJjaG9sZXN0ZXJvbGVtaWEgKGByaXNrNjE0YCkgZ3JvdXAgKG5vLCB5ZXMpCi0gQm94IGFuZCBXaGlza2VyIHBsb3QgZm9yIHRhcmdldChzKSBwbGFxdWUgbGV2ZWxzIGJ5IGxpcGlkLWxvd2VyaW5nIGRydWdzIGdyb3VwIChubywgeWVzKQotIEJveCBhbmQgV2hpc2tlciBwbG90IGZvciB0YXJnZXQocykgcGxhcXVlIGxldmVscyBieSBMREwtbGV2ZWxzIChtbW9sL0wpIGdyb3VwICg8MTAwLCAxMDAtMTI5LCAxMzAtMTU5LCAxNjAtMTg5LCAxOTArKQoKYGBge3IgTERMR3JvdXBzfQpsaWJyYXJ5KGRwbHlyKQoKQUVSTkFTRS5jbGluLmhkYWM5IDwtIEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgbXV0YXRlKExETEdyb3VwID0gZmFjdG9yKGNhc2Vfd2hlbihMRExfZmluYWxDVSA8IDEwMCB+ICI8MTAwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMRExfZmluYWxDVSA+PSAxMDAgICYgTERMX2ZpbmFsQ1UgPD0gMTI5IH4gIjEwMC0xMjkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExETF9maW5hbENVID49IDEzMCAgJiBMRExfZmluYWxDVSA8PSAxNTkgfiAiMTMwLTE1OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTERMX2ZpbmFsQ1UgPj0gMTYwICAmIExETF9maW5hbENVIDw9IDE4OSB+ICIxNjAtMTg5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMRExfZmluYWxDVSA+PSAxOTAgfiAiMTkwKyIpKSkgCgoKdGFibGUoQUVSTkFTRS5jbGluLmhkYWM5JExETEdyb3VwLCBBRVJOQVNFLmNsaW4uaGRhYzkkR2VuZGVyKQoKYGBgCgoKYGBge3IgRml4IEh5cGVyY2hvbGVzdGVyb2xlbWlhLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpyZXF1aXJlKHNqbGFiZWxsZWQpCgpBRVJOQVNFLmNsaW4uaGRhYzkkcmlzazYxNCA8LSB0b19mYWN0b3IoQUVSTkFTRS5jbGluLmhkYWM5JHJpc2s2MTQpCgojIEZpeCBwbGFxdWVwaGVub3R5cGVzCmF0dGFjaChBRVJOQVNFLmNsaW4uaGRhYzkpCkFFUk5BU0UuY2xpbi5oZGFjOVssIkh5cGVyY2hvbGVzdGVyb2xlbWlhIl0gPC0gTkEKQUVSTkFTRS5jbGluLmhkYWM5JEh5cGVyY2hvbGVzdGVyb2xlbWlhW3Jpc2s2MTQgPT0gIm1pc3NpbmcgdmFsdWUiXSA8LSBOQQpBRVJOQVNFLmNsaW4uaGRhYzkkSHlwZXJjaG9sZXN0ZXJvbGVtaWFbcmlzazYxNCA9PSAtOTk5XSA8LSBOQQpBRVJOQVNFLmNsaW4uaGRhYzkkSHlwZXJjaG9sZXN0ZXJvbGVtaWFbcmlzazYxNCA9PSAwXSA8LSAibm8iCkFFUk5BU0UuY2xpbi5oZGFjOSRIeXBlcmNob2xlc3Rlcm9sZW1pYVtyaXNrNjE0ID09IDFdIDwtICJ5ZXMiCmRldGFjaChBRVJOQVNFLmNsaW4uaGRhYzkpCgp0YWJsZShBRVJOQVNFLmNsaW4uaGRhYzkkcmlzazYxNCwgQUVSTkFTRS5jbGluLmhkYWM5JEh5cGVyY2hvbGVzdGVyb2xlbWlhKQoKIyBBRURCLnRlbXAgPC0gc3Vic2V0KEFFREIsICBzZWxlY3QgPSBjKCJTVFVEWV9OVU1CRVIiLCAiVVBJRCIsICJBZ2UiLCAiR2VuZGVyIiwgIkhvc3BpdGFsIiwgIkFydGVyeV9zdW1tYXJ5IiwgInJpc2s2MTQiLCAiSHlwZXJjaG9sZXN0ZXJvbGVtaWEiKSkKIyByZXF1aXJlKGxhYmVsbGVkKQojIEFFREIudGVtcCRHZW5kZXIgPC0gdG9fZmFjdG9yKEFFREIudGVtcCRHZW5kZXIpCiMgQUVEQi50ZW1wJEhvc3BpdGFsIDwtIHRvX2ZhY3RvcihBRURCLnRlbXAkSG9zcGl0YWwpCiMgQUVEQi50ZW1wJEFydGVyeV9zdW1tYXJ5IDwtIHRvX2ZhY3RvcihBRURCLnRlbXAkQXJ0ZXJ5X3N1bW1hcnkpCiMgCiMgRFQ6OmRhdGF0YWJsZShBRURCLnRlbXBbMToxMCxdLCBjYXB0aW9uID0gIkV4Y2VycHQgb2YgdGhlIHdob2xlIEFFREIuIiwgcm93bmFtZXMgPSBGQUxTRSkKIyAKIyBybShBRURCLnRlbXApCgpgYGAKCk5vdyB3ZSBjYW4gZHJhdyBzb21lIGdyYXBocyBvZiBwbGFxdWUgdGFyZ2V0KHMpIGxldmVscyBwZXIgc2V4IGFuZCBoeXBlcmNob2xlc3Rlcm9sZW1pYS9MREwtbGV2ZWxzIGdyb3VwLCBhcyB3ZWxsIGFzIHN0cmF0aWZpZWQgYnkgbGlwaWQtbG93ZXJpbmcgZHJ1Z3MgdXNlcnMgYXMgbWVkaWFuIMKxIGludGVycXVhcnRpbGUgcmFuZ2UuCgpgYGB7ciBwZXIgSHlwZXJjaG9sZXN0ZXJvbGVtaWEgcGVyIFNleH0KCmNvbXBhcmVfbWVhbnMoSERBQzkgfiBMRExHcm91cCwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShMRExHcm91cCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKExETEdyb3VwKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiTERMR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkxETCAobWcvZEwpIHBlciBnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkxETEdyb3VwIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuTERMR3JvdXBzLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhEQUM5IH4gTERMR3JvdXAsIGdyb3VwLmJ5ID0gIkdlbmRlciIsIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoTERMR3JvdXApKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShMRExHcm91cCkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIkxETEdyb3VwIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJMREwgKG1nL2RMKSBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSkiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiI0Q1MjY3QiIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IEdlbmRlciksIGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uaGRhYzkuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5MRExHcm91cHNfYnlHZW5kZXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCgoKY29tcGFyZV9tZWFucyhIREFDOSB+IEh5cGVyY2hvbGVzdGVyb2xlbWlhLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKEh5cGVyY2hvbGVzdGVyb2xlbWlhKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoSHlwZXJjaG9sZXN0ZXJvbGVtaWEpKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJIeXBlcmNob2xlc3Rlcm9sZW1pYSIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiRGlhZ25vc2VkIGh5cGVyY2hvbGVzdGVyb2xlbWlhIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSkiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJIeXBlcmNob2xlc3Rlcm9sZW1pYSIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkh5cGVyY2hvbGVzdGVyb2xlbWlhLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhEQUM5IH4gSHlwZXJjaG9sZXN0ZXJvbGVtaWEsIGdyb3VwLmJ5ID0gIkdlbmRlciIsIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoSHlwZXJjaG9sZXN0ZXJvbGVtaWEpKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShIeXBlcmNob2xlc3Rlcm9sZW1pYSkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIkh5cGVyY2hvbGVzdGVyb2xlbWlhIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJEaWFnbm9zZWQgaHlwZXJjaG9sZXN0ZXJvbGVtaWEgcGVyIGdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikpIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuSHlwZXJjaG9sZXN0ZXJvbGVtaWFfYnlHZW5kZXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCgpjb21wYXJlX21lYW5zKEhEQUM5IH4gTWVkLlN0YXRpbi5MTEQsIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoTWVkLlN0YXRpbi5MTEQpKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShNZWQuU3RhdGluLkxMRCkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIk1lZC5TdGF0aW4uTExEIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJMaXBpZC1sb3dlcmluZyBkcnVnIHVzZSIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikpIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiTWVkLlN0YXRpbi5MTEQiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uaGRhYzkuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5NZWQuU3RhdGluLkxMRC5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKY29tcGFyZV9tZWFucyhIREFDOSB+IE1lZC5TdGF0aW4uTExELCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKE1lZC5TdGF0aW4uTExEKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoTWVkLlN0YXRpbi5MTEQpKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJNZWQuU3RhdGluLkxMRCIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiTGlwaWQtbG93ZXJpbmcgZHJ1ZyB1c2UgcGVyIGdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikpIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuTWVkLlN0YXRpbi5MTERfYnlHZW5kZXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCgoKCmNvbXBhcmVfbWVhbnMoSERBQzkgfiBMRExHcm91cCwgZ3JvdXAuYnkgPSAiTWVkLlN0YXRpbi5MTEQiLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKExETEdyb3VwKSAmICFpcy5uYShNZWQuU3RhdGluLkxMRCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKExETEdyb3VwKSAmICFpcy5uYShNZWQuU3RhdGluLkxMRCkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIkxETEdyb3VwIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJMREwgKG1nL2RMKSBwZXIgTExEIHVzZSIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikpIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiTWVkLlN0YXRpbi5MTEQiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiIzQ5QTAxRCIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IE1lZC5TdGF0aW4uTExEKSwgbGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkxETEdyb3Vwc19ieU1lZC5TdGF0aW4uTExELnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhEQUM5IH4gSHlwZXJjaG9sZXN0ZXJvbGVtaWEsIGdyb3VwLmJ5ID0gIk1lZC5TdGF0aW4uTExEIiwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShIeXBlcmNob2xlc3Rlcm9sZW1pYSkgJiAhaXMubmEoTWVkLlN0YXRpbi5MTEQpKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShIeXBlcmNob2xlc3Rlcm9sZW1pYSkgJiAhaXMubmEoTWVkLlN0YXRpbi5MTEQpKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJIeXBlcmNob2xlc3Rlcm9sZW1pYSIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiRGlhZ25vc2VkIGh5cGVyY2hvbGVzdGVyb2xlbWlhIHBlciBMTEQgdXNlIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSkiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJNZWQuU3RhdGluLkxMRCIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjNDlBMDFEIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gTWVkLlN0YXRpbi5MTEQpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuTERMR3JvdXBzX2J5TWVkLlN0YXRpbi5MTEQucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCgpgYGAKCgoKIyMgS2lkbmV5IGZ1bmN0aW9uIChlR0ZSKQpXZSB3YW50IHRvIGNyZWF0ZSBmaWd1cmVzIG9mIHRhcmdldChzKSBsZXZlbHMgc3RyYXRpZmllZCBieSBraWRuZXkgZnVuY3Rpb24uIAoKLSBCb3ggYW5kIFdoaXNrZXIgcGxvdCBmb3IgdGFyZ2V0KHMpIHBsYXF1ZSBsZXZlbHMgYnkgY2hyb25pYyBraWRuZXkgZGlzZWFzZSAoQ0tEKSBncm91cCAoMSwgMiwgMywgNCwgNSkKLSBCb3ggYW5kIFdoaXNrZXIgcGxvdCBmb3IgdGFyZ2V0KHMpIHBsYXF1ZSBsZXZlbHMgYnkgZUdGUiAoTURSRC1iYXNlZCkgZ3JvdXAgKDkwKywgNjAtODksIDMwLTU5LCA8MzApCgpgYGB7ciBFR0ZSfQpsaWJyYXJ5KGRwbHlyKQoKQUVSTkFTRS5jbGluLmhkYWM5IDwtIEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgbXV0YXRlKGVHRlJHcm91cCA9IGZhY3RvcihjYXNlX3doZW4oR0ZSX01EUkQgPCAxNSB+ICI8MTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR0ZSX01EUkQgPj0gMTUgICYgR0ZSX01EUkQgPD0gMjkgfiAiMTUtMjkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR0ZSX01EUkQgPj0gMzAgICYgR0ZSX01EUkQgPD0gNTkgfiAiMzAtNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR0ZSX01EUkQgPj0gNjAgICYgR0ZSX01EUkQgPD0gODkgfiAiNjAtODkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR0ZSX01EUkQgPj0gOTAgfiAiOTArIikpKQoKdGFibGUoQUVSTkFTRS5jbGluLmhkYWM5JGVHRlJHcm91cCwgQUVSTkFTRS5jbGluLmhkYWM5JEdlbmRlcikKCnRhYmxlKEFFUk5BU0UuY2xpbi5oZGFjOSRlR0ZSR3JvdXAsIEFFUk5BU0UuY2xpbi5oZGFjOSRLRE9RSSkKCmBgYAoKTm93IHdlIGNhbiBkcmF3IHNvbWUgZ3JhcGhzIG9mIHBsYXF1ZSB0YXJnZXQocykgbGV2ZWxzIHBlciBzZXggYW5kIGtpZG5leSBmdW5jdGlvbiBncm91cCBhcyBtZWRpYW4gwrEgaW50ZXJxdWFydGlsZSByYW5nZS4KCgpgYGB7ciBwZXIgRUdGUiBwZXIgU2V4fQoKIyBHbG9iYWwgdGVzdAoKY29tcGFyZV9tZWFucyhIREFDOSB+IGVHRlJHcm91cCwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShlR0ZSR3JvdXApKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShlR0ZSR3JvdXApKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJlR0ZSR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gImVHRlIgKG1ML21pbiBwZXIgMS43MyBtMikiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZUdGUkdyb3VwIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkVHRlIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSERBQzkgfiBlR0ZSR3JvdXAsIGdyb3VwLmJ5ID0gIkdlbmRlciIsICBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKGVHRlJHcm91cCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKGVHRlJHcm91cCkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoImVHRlJHcm91cCIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiZUdGUiAobUwvbWluIHBlciAxLjczIG0yKSBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gR2VuZGVyKSwgbGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkVHRlJfYnlHZW5kZXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSERBQzkgfiBLRE9RSSwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShLRE9RSSkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDEgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKEtET1FJKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiS0RPUUkiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIktpZG5leSBmdW5jdGlvbiAoS0RPUUkpIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIktET1FJIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBLRE9RSSksIGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncGFyKHAxICsgcm90YXRlX3hfdGV4dCg0NSksIGxlZ2VuZCA9ICJyaWdodCIpIApybShwMSkKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLktET1FJLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhEQUM5IH4gS0RPUUksIGdyb3VwLmJ5ID0gIkdlbmRlciIsICAgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShLRE9RSSkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDEgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKEtET1FJKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiS0RPUUkiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIktpZG5leSBmdW5jdGlvbiAoS0RPUUkpIHBlciBnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3BhcihwMSArIHJvdGF0ZV94X3RleHQoNDUpLCBsZWdlbmQgPSAicmlnaHQiKSAKcm0ocDEpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uaGRhYzkuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5LRE9RSV9ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKY29tcGFyZV9tZWFucyhIREFDOSB+IGVHRlJHcm91cCwgIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoZUdGUkdyb3VwKSAmICFpcy5uYShLRE9RSSkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDEgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKGVHRlJHcm91cCkgJiAhaXMubmEoS0RPUUkpKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJlR0ZSR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gImVHRlIgKG1ML21pbiBwZXIgMS43MyBtMikgYnkgS0RPUUkgZ3JvdXAiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiS0RPUUkiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3BhcihwMSwgbGVnZW5kID0gInJpZ2h0IikKcm0ocDEpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uaGRhYzkuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5FR0ZSX0tET1FJLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpgYGAKCgoKIyMgQk1JCldlIHdhbnQgdG8gY3JlYXRlIGZpZ3VyZXMgb2YgdGFyZ2V0KHMpIGxldmVscyBzdHJhdGlmaWVkIGJ5IEJNSS4gCgotIEJveCBhbmQgV2hpc2tlciBwbG90IGZvciB0YXJnZXQocykgcGxhcXVlIGxldmVscyBieSBCTUkgV0hPIGdyb3VwICh1bmRlcndlaWdodCwgbm9ybWFsLCBvdmVyd2VpZ2h0LCBvYmVzZSkKLSBCb3ggYW5kIFdoaXNrZXIgcGxvdCBmb3IgdGFyZ2V0KHMpIHBsYXF1ZSBsZXZlbHMgYnkgQk1JIGdyb3VwICg8MTguNSwgMTguNS0yNC45LCAyNSwgMjkuOSwgMzAtMjQuOSwgMzUrKQoKYGBge3IgQk1JfQpsaWJyYXJ5KGRwbHlyKQoKQUVSTkFTRS5jbGluLmhkYWM5IDwtIEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgbXV0YXRlKEJNSUdyb3VwID0gZmFjdG9yKGNhc2Vfd2hlbihCTUkgPCAxOC41IH4gIjwxOC41IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCTUkgPj0gMTguNSAgJiBCTUkgPCAyNSB+ICIxOC41LTI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCTUkgPj0gMjUgICYgQk1JIDwgMzAgfiAiMjUtMjkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJNSSA+PSAzMCAgJiBCTUkgPCAzNSB+ICIzMC0zNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQk1JID49IDM1IH4gIjM1KyIpKSkgCgojIHJlcXVpcmUobGFiZWxsZWQpCiMgQUVSTkFTRS5jbGluLmhkYWM5JEJNSV9VUyA8LSBhc19mYWN0b3IoQUVSTkFTRS5jbGluLmhkYWM5JEJNSV9VUykKIyBBRVJOQVNFLmNsaW4uaGRhYzkkQk1JX1dITyA8LSBhc19mYWN0b3IoQUVSTkFTRS5jbGluLmhkYWM5JEJNSV9XSE8pCiMgdGFibGUoQUVSTkFTRS5jbGluLmhkYWM5JEJNSV9XSE8sIEFFUk5BU0UuY2xpbi5oZGFjOSRCTUlfVVMpCgp0YWJsZShBRVJOQVNFLmNsaW4uaGRhYzkkQk1JR3JvdXAsIEFFUk5BU0UuY2xpbi5oZGFjOSRHZW5kZXIpCnRhYmxlKEFFUk5BU0UuY2xpbi5oZGFjOSRCTUlHcm91cCwgQUVSTkFTRS5jbGluLmhkYWM5JEJNSV9XSE8pCgpgYGAKCk5vdyB3ZSBjYW4gZHJhdyBzb21lIGdyYXBocyBvZiBwbGFxdWUgTUNQMSBsZXZlbHMgcGVyIHNleCBhbmQgYWdlIGdyb3VwIGFzIG1lZGlhbiDCsSBpbnRlcnF1YXJ0aWxlIHJhbmdlLgoKCmBgYHtyIHBlciBCTUkgcGVyIFNleH0KCiMgR2xvYmFsIHRlc3QKY29tcGFyZV9tZWFucyhIREFDOSB+IEJNSUdyb3VwLCAgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShCTUlHcm91cCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKEJNSUdyb3VwKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiQk1JR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkJNSSBncm91cHMgKGtnL20yKSIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikiLAogICAgICAgICAgICAgICAgICAjIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgICMgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJCTUlHcm91cCIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkJNSS5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKY29tcGFyZV9tZWFucyhIREFDOSB+IEJNSUdyb3VwLCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKEJNSUdyb3VwKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoQk1JR3JvdXApKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJCTUlHcm91cCIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiQk1JIGdyb3VwcyAoa2cvbTIpIHBlciBnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuQk1JX2J5R2VuZGVyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhEQUM5IH4gQk1JR3JvdXAsICBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKEJNSUdyb3VwKSAmICFpcy5uYShCTUlfV0hPKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpwMSA8LSBnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoQk1JR3JvdXApICYgIWlzLm5hKEJNSV9XSE8pKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJCTUlHcm91cCIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiQk1JIGdyb3VwcyAoa2cvbTIpIHBlciBXSE8gY2F0ZWdvcmllcyIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJCTUlfV0hPIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwYXIocDEsIGxlZ2VuZCA9ICJyaWdodCIpCnJtKHAxKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuQk1JX2J5V0hPLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpgYGAKCgojIyBEaWFiZXRlcwpXZSB3YW50IHRvIGNyZWF0ZSBmaWd1cmVzIG9mIHRhcmdldChzKSBsZXZlbHMgc3RyYXRpZmllZCBieSB0eXBlIDIgZGlhYmV0ZXMuIAoKLSBCb3ggYW5kIFdoaXNrZXIgcGxvdCBmb3IgdGFyZ2V0KHMpIHBsYXF1ZSBsZXZlbHMgYnkgdHlwZSAyIGRpYWJldGVzIGdyb3VwIChubywgeWVzKQoKTm93IHdlIGNhbiBkcmF3IHNvbWUgZ3JhcGhzIG9mIHBsYXF1ZSB0YXJnZXQocykgbGV2ZWxzIHBlciBzZXggYW5kIGFnZSBncm91cCBhcyBtZWRpYW4gwrEgaW50ZXJxdWFydGlsZSByYW5nZS4KCgpgYGB7ciBwZXIgRGlhYmV0ZXMgcGVyIFNleH0KCiMgR2xvYmFsIHRlc3QKY29tcGFyZV9tZWFucyhIREFDOSB+IERpYWJldGVzU3RhdHVzLCAgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShEaWFiZXRlc1N0YXR1cykpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKERpYWJldGVzU3RhdHVzKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiRGlhYmV0ZXNTdGF0dXMiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkRpYWJldGVzIHN0YXR1cyIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikiLAogICAgICAgICAgICAgICAgICAjIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgICMgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJEaWFiZXRlc1N0YXR1cyIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gYygibWVkaWFuX2lxciIsICJqaXR0ZXIiKSkgKwogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuRGlhYmV0ZXMucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSERBQzkgfiBEaWFiZXRlc1N0YXR1cywgZ3JvdXAuYnkgPSAiR2VuZGVyIiwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShEaWFiZXRlc1N0YXR1cykpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKERpYWJldGVzU3RhdHVzKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiRGlhYmV0ZXNTdGF0dXMiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkRpYWJldGVzIHN0YXR1cyBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gYygibWVkaWFuX2lxciIsICJqaXR0ZXIiKSkgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuRGlhYmV0ZXNfYnlHZW5kZXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCgpgYGAKCgoKIyMgU21va2luZwpXZSB3YW50IHRvIGNyZWF0ZSBmaWd1cmVzIG9mIHRhcmdldChzKSBsZXZlbHMgc3RyYXRpZmllZCBieSBzbW9raW5nLiAKCi0gQm94IGFuZCBXaGlza2VyIHBsb3QgZm9yIHRhcmdldChzKSBwbGFxdWUgbGV2ZWxzIGJ5IHNtb2tpbmcgZ3JvdXAgKG5ldmVyLCBleCwgY3VycmVudCkKCk5vdyB3ZSBjYW4gZHJhdyBzb21lIGdyYXBocyBvZiBwbGFxdWUgdGFyZ2V0KHMpIGxldmVscyBwZXIgc2V4IGFuZCBhZ2UgZ3JvdXAgYXMgbWVkaWFuIMKxIGludGVycXVhcnRpbGUgcmFuZ2UuCgoKYGBge3IgcGVyIFNtb2tpbmcgcGVyIFNleH0KCiMgR2xvYmFsIHRlc3QKY29tcGFyZV9tZWFucyhIREFDOSB+IFNtb2tlclN0YXR1cywgIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoU21va2VyU3RhdHVzKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoU21va2VyU3RhdHVzKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiU21va2VyU3RhdHVzIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJTbW9rZXIgc3RhdHVzIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgICMgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgIyBwYWxldHRlID0gYygiI0Q1MjY3QiIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIlNtb2tlclN0YXR1cyIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gYygibWVkaWFuX2lxciIsICJqaXR0ZXIiKSkgKwogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuU21va2luZy5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKY29tcGFyZV9tZWFucyhIREFDOSB+IFNtb2tlclN0YXR1cywgZ3JvdXAuYnkgPSJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKFNtb2tlclN0YXR1cykpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKFNtb2tlclN0YXR1cykpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIlNtb2tlclN0YXR1cyIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiU21va2VyIHN0YXR1cyBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gYygibWVkaWFuX2lxciIsICJqaXR0ZXIiKSkgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuU21va2luZ19ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKYGBgCgoKCiMjIFN0ZW5vc2lzCldlIHdhbnQgdG8gY3JlYXRlIGZpZ3VyZXMgb2YgdGFyZ2V0KHMpIGxldmVscyBzdHJhdGlmaWVkIGJ5IHN0ZW5vc2lzIGdyYWRlLiAKCi0gQm94IGFuZCBXaGlza2VyIHBsb3QgZm9yIHRhcmdldChzKSBwbGFxdWUgbGV2ZWxzIGJ5IHN0ZW5vc2lzIGdyYWRlIGdyb3VwICg8NzAsIDcwLTg5LCA5MCspCgpgYGB7ciBTdGVub3Npc30KbGlicmFyeShkcGx5cikKCkFFUk5BU0UuY2xpbi5oZGFjOSA8LSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIG11dGF0ZShTdGVub3RpY0dyb3VwID0gZmFjdG9yKGNhc2Vfd2hlbihzdGVub3NlID09ICIwLTQ5JSIgfiAiPDcwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVub3NlID09ICIwLTQ5JSIgfiAiPDcwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVub3NlID09ICI1MC03MCUiIH4gIjw3MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Rlbm9zZSA9PSAiNzAtOTAlIiB+ICI3MC04OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Rlbm9zZSA9PSAiNTAtOTklIiB+ICI5MCsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZW5vc2UgPT0gIjcwLTk5JSIgfiAiOTArIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVub3NlID09ICIxMDAlIChPY2NsdXNpb24pIiB+ICI5MCsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZW5vc2UgPT0gIjkwLTk5JSIgfiAiOTArIikpKQoKdGFibGUoQUVSTkFTRS5jbGluLmhkYWM5JFN0ZW5vdGljR3JvdXAsIEFFUk5BU0UuY2xpbi5oZGFjOSRHZW5kZXIpCnRhYmxlKEFFUk5BU0UuY2xpbi5oZGFjOSRzdGVub3NlLCBBRVJOQVNFLmNsaW4uaGRhYzkkU3Rlbm90aWNHcm91cCkKCmBgYAoKTm93IHdlIGNhbiBkcmF3IHNvbWUgZ3JhcGhzIG9mIHBsYXF1ZSB0YXJnZXQocykgbGV2ZWxzIHBlciBzZXggYW5kIGFnZSBncm91cCBhcyBtZWRpYW4gwrEgaW50ZXJxdWFydGlsZSByYW5nZS4KCmBgYHtyIHBlciBTdGVub3NpcyBwZXIgU2V4fQoKIyBHbG9iYWwgdGVzdApjb21wYXJlX21lYW5zKEhEQUM5IH4gU3Rlbm90aWNHcm91cCwgIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoU3Rlbm90aWNHcm91cCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKFN0ZW5vdGljR3JvdXApKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJTdGVub3RpY0dyb3VwIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJTdGVub3RpYyBncmFkZSIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikiLAogICAgICAgICAgICAgICAgICAjIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgICMgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJTdGVub3RpY0dyb3VwIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuU3Rlbm9zaXMucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSERBQzkgfiBTdGVub3RpY0dyb3VwLCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKFN0ZW5vdGljR3JvdXApKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShTdGVub3RpY0dyb3VwKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiU3Rlbm90aWNHcm91cCIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiU3Rlbm90aWMgZ3JhZGUgcGVyIGdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiI0Q1MjY3QiIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IEdlbmRlciksIGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uaGRhYzkuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5TdGVub3Npc19ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKYGBgCgoKIyMgU3ltcHRvbXMKV2Ugd2FudCB0byBjcmVhdGUgcGVyLXN5bXB0b20gZmlndXJlcy4gCgpgYGB7ciBTeW1wdG9tR3JvdXBzfQpsaWJyYXJ5KGRwbHlyKQoKdGFibGUoQUVSTkFTRS5jbGluLmhkYWM5JEFnZUdyb3VwLCBBRVJOQVNFLmNsaW4uaGRhYzkkQXN5bXB0U3ltcHQyRykKdGFibGUoQUVSTkFTRS5jbGluLmhkYWM5JEdlbmRlciwgQUVSTkFTRS5jbGluLmhkYWM5JEFzeW1wdFN5bXB0MkcpCnRhYmxlKEFFUk5BU0UuY2xpbi5oZGFjOSRBc3ltcHRTeW1wdDJHKQoKYGBgCgpOb3cgd2UgY2FuIGRyYXcgc29tZSBncmFwaHMgb2YgcGxhcXVlIHRhcmdldChzKSBsZXZlbHMgcGVyIHN5bXB0b20gZ3JvdXAgYXMgbWVkaWFuIMKxIGludGVycXVhcnRpbGUgcmFuZ2UuCgpgYGB7ciBwZXIgU3ltcHRvbUdyb3Vwc30KCiMgP2dncHVicjo6Z2dib3hwbG90KCkKbXlfY29tcGFyaXNvbnMgPC0gbGlzdChjKCJBc3ltcHRvbWF0aWMiLCAiU3ltcHRvbWF0aWMiKSkKCmNvbXBhcmVfbWVhbnMoSERBQzkgfiBBc3ltcHRTeW1wdDJHLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5LCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDEgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5LCAKICAgICAgICAgICAgICAgICAgeCA9ICJBc3ltcHRTeW1wdDJHIiwgeSA9ICJIREFDOSIsCiAgICAgICAgICAgICAgICAgIHRpdGxlID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pIGxldmVscyBwZXIgc3ltcHRvbSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIlN5bXB0b21zIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkFzeW1wdFN5bXB0MkciLCAKICAgICAgICAgICAgICAgICAgIyBwYWxldHRlID0gYyh1aXRob2ZfY29sb3JbMTZdLCB1aXRob2ZfY29sb3JbMjNdKSwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICBhZGQgPSAiZG90cGxvdCIsICMgQWRkIGRvdHBsb3QKICAgICAgICAgICAgICAgICAgYWRkLnBhcmFtcyA9IGxpc3QoYmlud2lkdGggPSAwLjEsIGRvdHNpemUgPSAwLjMpCiAgICAgICAgICApICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoY29tcGFyaXNvbnMgPSBteV9jb21wYXJpc29ucywgbWV0aG9kID0gIndpbGNveC50ZXN0IikKZ2dwYXIocDEsIGxlZ2VuZCA9IGMoInJpZ2h0IiksIGxlZ2VuZC50aXRsZSA9ICJTeW1wdG9tcyIpCgpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuQXN5bXB0U3ltcHQyRy5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKcm0ocDEpCmBgYAoKYGBge3IgcGVyIFN5bXB0b21Hcm91cHMgYnkgR2VuZGVyfQpjb21wYXJlX21lYW5zKEhEQUM5IH4gQXN5bXB0U3ltcHQyRywgZ3JvdXAuYnkgPSAiR2VuZGVyIiwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCnAxIDwtIGdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSwgCiAgICAgICAgICAgICAgICAgIHggPSAiQXN5bXB0U3ltcHQyRyIsIHkgPSAiSERBQzkiLAogICAgICAgICAgICAgICAgICB0aXRsZSA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKSBsZXZlbHMgcGVyIHN5bXB0b20gYnkgZ2VuZGVyIiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiU3ltcHRvbXMiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiZG90cGxvdCIsICMgQWRkIGRvdHBsb3QKICAgICAgICAgICAgICAgICAgYWRkLnBhcmFtcyA9IGxpc3QoYmlud2lkdGggPSAwLjEsIGRvdHNpemUgPSAwLjMpCiAgICAgICAgICApICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gR2VuZGVyKSwgbGFiZWwgPSAicC5mb3JtYXQiLCAgbWV0aG9kID0gIndpbGNveC50ZXN0IikKZ2dwYXIocDEsIGxlZ2VuZCA9IGMoInJpZ2h0IiksIGxlZ2VuZC50aXRsZSA9ICJTeW1wdG9tcyIpCgpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuQXN5bXB0U3ltcHQyRy5ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKcm0ocDEpCgpgYGAKCiMjIyBBbHRlcm5hdGl2ZSBncmFwaApgYGB7cn0KIyA/Z2dwdWJyOjpnZ2JveHBsb3QoKQojIGNvbXBhcmVfbWVhbnMoTFJDSDEgfiBlR0ZSR3JvdXAsIGRhdGEgPSBBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoZUdGUkdyb3VwKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQojIGdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShlR0ZSR3JvdXApKQoKY2F0KCJHZXQgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciB0YXJnZXQ6XG4iKQpzdW1tYXJ5KEFFUk5BU0UuY2xpbi5oZGFjOSRIREFDOSkKCmNhdCgiXG5Db3VudCBudW1iZXIgb2YgdmFsdWVzID4gMTAwIGZvciB0YXJnZXQ6XG4iKQpzdW0oQUVSTkFTRS5jbGluLmhkYWM5JEhEQUM5ID4gMTAwKQoKY2F0KCJcblNldHRpbmcgdmFsdWVzID4gMTAwIGZvciB0YXJnZXQgdG8gMTAwLlxuIikKdGVtcCA8LSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIAogIGZpbHRlcighaXMubmEoQXN5bXB0U3ltcHQyRykpCgp0ZW1wJEhEQUM5W3RlbXAkSERBQzkgPiAxMDBdIDwtIDEwMAoKY2F0KCJHZXQgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciB0YXJnZXQgYWZ0ZXIgZml4aW5nIHZhbHVlczpcbiIpCnN1bW1hcnkodGVtcCRIREFDOSkKCmBgYAoKCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KGMoIkFzeW1wdG9tYXRpYyIsICJTeW1wdG9tYXRpYyIpKQoKY29tcGFyZV9tZWFucyhIREFDOSB+IEFzeW1wdFN5bXB0MkcsIGRhdGEgPSB0ZW1wLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKCnAxIDwtIGdncHVicjo6Z2dib3hwbG90KHRlbXAsIAogICAgICAgICAgICAgICAgICB4ID0gIkFzeW1wdFN5bXB0MkciLCB5ID0gIkhEQUM5IiwKICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikgbGV2ZWxzIHBlciBzeW1wdG9tIiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiU3ltcHRvbXMiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiQXN5bXB0U3ltcHQyRyIsIAogICAgICAgICAgICAgICAgICAjIHBhbGV0dGUgPSBjKHVpdGhvZl9jb2xvclsxNl0sIHVpdGhvZl9jb2xvclsyM10pLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiLCAjIEFkZCBkb3RwbG90CiAgICAgICAgICAgICAgICAgIGFkZC5wYXJhbXMgPSBsaXN0KGJpbndpZHRoID0gMC4xLCBkb3RzaXplID0gMC4zKQogICAgICAgICAgKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIpCmdncGFyKHAxLCBsZWdlbmQgPSBjKCJyaWdodCIpLCBsZWdlbmQudGl0bGUgPSAiU3ltcHRvbXMiKQoKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSERBQzkucGxhcXVlLkFzeW1wdFN5bXB0Mkcubm9OQV9saW1pdENvdW50MTAwLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhEQUM5LnBsYXF1ZS5Bc3ltcHRTeW1wdDJHLm5vTkFfbGltaXRDb3VudDEwMC5wcyIpLCBwbG90ID0gbGFzdF9wbG90KCkpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhEQUM5LnBsYXF1ZS5Bc3ltcHRTeW1wdDJHLm5vTkFfbGltaXRDb3VudDEwMC5wbmciKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKcm0ocDEsIHRlbXApCmBgYAoKCgojIyBGb3Jlc3QgcGxvdHMKCldlIHdvdWxkIGFsc28gbGlrZSB0byB2aXN1YWxpemUgdGhlIG11bHRpdmFyaWFibGUgYW5hbHlzZXMgcmVzdWx0cy4KYGBge3IgbG9hZCBtb2RlbCBkYXRhfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkob3Blbnhsc3gpCm1vZGVsMV90YXJnZXQgPC0gcmVhZC54bHN4KHBhc3RlMChPVVRfbG9jLCAiLyIsIFRvZGF5LCAiLkFFUk5BU0UuY2xpbi5oZGFjOS5CaW4uVW5pLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5SQU5LLlN5bXB0b21zLk1PREVMMS54bHN4IikpCm1vZGVsMl90YXJnZXQgPC0gcmVhZC54bHN4KHBhc3RlMChPVVRfbG9jLCAiLyIsIFRvZGF5LCAiLkFFUk5BU0UuY2xpbi5oZGFjOS5CaW4uTXVsdGkuIixUUkFJVF9PRl9JTlRFUkVTVCwiLlJBTksuU3ltcHRvbXMuTU9ERUwyLnhsc3giKSkKCm1vZGVsMV90YXJnZXQkbW9kZWwgPC0gInVuaXZhcmlhdGUiCm1vZGVsMl90YXJnZXQkbW9kZWwgPC0gIm11bHRpdmFyaWF0ZSIKCm1vZGVsc190YXJnZXQgPC0gcmJpbmQobW9kZWwxX3RhcmdldCwgbW9kZWwyX3RhcmdldCkKbW9kZWxzX3RhcmdldAoKYGBgCgpGb3Jlc3QgcGxvdHMuCgpgYGB7ciBmb3Jlc3RwbG90IHBsYXF1ZSwgZXhwZXJpbWVudCAyfQpkYXQgPC0gZGF0YS5mcmFtZShncm91cCA9IGZhY3RvcihjKCJBZ2UsIHNleC1hZGp1c3RlZCIsICJBZ2UsIHNleCwgYW5kIGFkanVzdGVkIGZvciByaXNrIGZhY3RvcnMiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygiQWdlLCBzZXgsIGFuZCBhZGp1c3RlZCBmb3IgcmlzayBmYWN0b3JzIiwgIkFnZSwgc2V4LWFkanVzdGVkIikpLAogICAgICAgICAgICAgICAgICBjZW4gPSBjKG1vZGVsc190YXJnZXQkT1JbbW9kZWxzX3RhcmdldCRQcmVkaWN0b3I9PSJIREFDOSJdKSwKICAgICAgICAgICAgICAgICAgbG93ID0gYyhtb2RlbHNfdGFyZ2V0JGxvdzk1Q0lbbW9kZWxzX3RhcmdldCRQcmVkaWN0b3I9PSJIREFDOSJdKSwKICAgICAgICAgICAgICAgICAgaGlnaCA9IGMobW9kZWxzX3RhcmdldCR1cDk1Q0lbbW9kZWxzX3RhcmdldCRQcmVkaWN0b3I9PSJIREFDOSJdKSkKCmZwIDwtIGdncGxvdChkYXRhID0gZGF0LCBhZXMoeCA9IGdyb3VwLCB5ID0gY2VuLCB5bWluID0gbG93LCB5bWF4ID0gaGlnaCkpICsKICBnZW9tX3BvaW50cmFuZ2UobGluZXR5cGUgPSAyLCBzaXplID0gMSwgY29sb3VyID0gYygiIzEyOTBEOSIsICIjNDlBMDFEIikpICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMSwgbHR5ID0gMikgKyAgIyBhZGQgYSBkb3R0ZWQgbGluZSBhdCB4PTEgYWZ0ZXIgZmxpcAogIGNvb3JkX2ZsaXAoeWxpbSA9IGMoMC44LCAxLjcpKSArICAjIGZsaXAgY29vcmRpbmF0ZXMgKHB1dHMgbGFiZWxzIG9uIHkgYXhpcykKICB4bGFiKCJNb2RlbCIpICsgeWxhYigiT1IgKDk1JSBDSSkgZm9yIHN5bXB0b21hdGljIHBsYXF1ZXMiKSArCiAgZ2d0aXRsZSgiTm9ybWFsaXplZCBIREFDOSBleHByZXNzaW9uIikgKwogIHRoZW1lX21pbmltYWwoKSAgIyB1c2UgYSB3aGl0ZSBiYWNrZ3JvdW5kCnByaW50KGZwKQoKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLmZvcmVzdC5wZGYiKSwgcGxvdCA9IGZwKQoKcm0oZnApCmBgYAoKCiMjIEhEQUM5IHZzLiBjeXRva2luZXMgcGxhcXVlIGxldmVscyBjb3JyZWxhdGlvbnMKCldlIHdpbGwgcGxvdCB0aGUgY29ycmVsYXRpb25zIG9mIG90aGVyIGN5dG9raW5lIHBsYXF1ZSBsZXZlbHMgdG8gdGhlIE1DUDEgcGxhcXVlIGxldmVscy4gVGhlc2UgaW5jbHVkZToKCi0gSUwyCi0gSUw0Ci0gSUw1Ci0gSUw2Ci0gSUw4Ci0gSUw5Ci0gSUwxMAotIElMMTIKLSBJTDEzCi0gSUwyMQotIElORkcKLSBUTkZBCi0gTUlGCi0gTUNQMQotIE1JUDFhCi0gUkFOVEVTCi0gTUlHCi0gSVAxMAotIEVvdGF4aW4xCi0gVEFSQwotIFBBUkMKLSBNREMKLSBPUEcKLSBzSUNBTTEKLSBWRUdGQQotIFRHRkIKCkluIGFkZGl0aW9uIHdlIHdpbGwgbG9vayBhdCB0aHJlZSBtZXRhbGxvcHJvdGVpbmFzZXMgd2hpY2ggd2VyZSBtZWFzdXJlZCB1c2luZyBhbiBhY3Rpdml0eSBhc3NheS4gCgotIE1NUDIKLSBNTVA4Ci0gTU1QOQoKVGhlIHByb3RlaW5zIHdlcmUgbWVhc3VyZWQgdXNpbmcgRkFDUyBhbmQgTFVNSU5FWC4gR2l2ZW4gdGhlIGRpZmZlcmVudCBwbGF0Zm9ybXMgdXNlZCAoRkFDUyB2cy4gTFVNSU5FWCksIHdlIHdpbGwgaW52ZXJzZSByYW5rLW5vcm1hbGl6ZSB0aGVzZSB2YXJpYWJsZXMgYXMgd2VsbCB0byBzY2FsZSB0aGVtIHRvIHRoZSBzYW1lIHNjYWxlIGFzIHRoZSB0YXJnZXQocylgIHBsYXF1ZSBsZXZlbHMuCgoKV2Ugd2lsbCBzZXQgdGhlIG1lYXN1cmVtZW50cyB0aGF0IHlpZWxkZWQgJzAnIHRvIE5BLCBhcyBpdCBpcyB1bmxpa2VseSB0aGF0IGFueSBwcm90ZWluIGV2ZXIgaGFzIGV4YWN0bHkgMCBjb3BpZXMuIFRoZSAnMCcgeWllbGRlZCBkdXJpbmcgdGhlIGV4cGVyaW1lbnQgYXJlIGR1ZSB0byB0aGUgbGltaXRzIG9mIHRoZSBkZXRlY3Rpb24uCgojIyMgUHJlcGFyZSBkYXRhCgpgYGB7cn0KIyBmaXggbmFtZXMKbmFtZXMoQUVEQi5DRUEpW25hbWVzKEFFREIuQ0VBKSA9PSAiVkVGR0EiXSA8LSAiVkVHRkEiCgojIGZpeCBuYW1lcwpuYW1lcyhBRVJOQVNFLmNsaW4uaGRhYzkpW25hbWVzKEFFUk5BU0UuY2xpbi5oZGFjOSkgPT0gIklMNiJdIDwtICJJTDZybmEiCgpjeXRva2luZXMgPC0gYygiSUwyIiwgIklMNCIsICJJTDUiLCAiSUw2IiwgIklMOCIsICJJTDkiLCAiSUwxMCIsICJJTDEyIiwgIklMMTMiLCAiSUwyMSIsIAogICAgICAgICAgICAgICAiSU5GRyIsICJUTkZBIiwgIk1JRiIsICJNQ1AxIiwgIk1JUDFhIiwgIlJBTlRFUyIsICJNSUciLCAiSVAxMCIsICJFb3RheGluMSIsIAogICAgICAgICAgICAgICAiVEFSQyIsICJQQVJDIiwgIk1EQyIsICJPUEciLCAic0lDQU0xIiwgIlZFR0ZBIiwgIlRHRkIiKQptZXRhbGxvcHJvdGVpbmFzZXMgPC0gYygiTU1QMiIsICJNTVA4IiwgIk1NUDkiKQoKCkFFUk5BU0UuY2xpbi5oZGFjOSA8LSBtZXJnZShBRVJOQVNFLmNsaW4uaGRhYzksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0KEFFREIuQ0VBLCBzZWxlY3QgPSBjKCJTVFVEWV9OVU1CRVIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjeXRva2luZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWxsb3Byb3RlaW5hc2VzKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkueCA9ICJTVFVEWV9OVU1CRVIiLCBieS55ID0gIlNUVURZX05VTUJFUiIsIHNvcnQgPSBUUlVFLCBhbGwueCA9IFRSVUUpCgpgYGAKCgpgYGB7ciBIREFDOSB2cyBDeXRva2luZXMgSU5SVCwgcGFnZWQucHJpbnQ9VFJVRX0KCnByb3RlaW5zX29mX2ludGVyZXN0IDwtIGMoY3l0b2tpbmVzLCBtZXRhbGxvcHJvdGVpbmFzZXMpCgpwcm90ZWluc19vZl9pbnRlcmVzdF9yYW5rID0gdW5saXN0KGxhcHBseShwcm90ZWluc19vZl9pbnRlcmVzdCwgcGFzdGUwLCAiX3JhbmsiKSkKCiMgbWFrZSB2YXJpYWJsZXMgbnVtZXJpY3MoKQpBRVJOQVNFLmNsaW4uaGRhYzkgPC0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JQogIG11dGF0ZV9lYWNoKGZ1bnMoYXMubnVtZXJpYyksIHByb3RlaW5zX29mX2ludGVyZXN0KQogIApmb3IoUFJPVEVJTiBpbiAxOmxlbmd0aChwcm90ZWluc19vZl9pbnRlcmVzdCkpewoKICAjIFVDT1JCSU9HU0FxYyRaIDwtIE5VTEwKICB2YXIudGVtcC5yYW5rID0gcHJvdGVpbnNfb2ZfaW50ZXJlc3RfcmFua1tQUk9URUlOXQogIHZhci50ZW1wID0gcHJvdGVpbnNfb2ZfaW50ZXJlc3RbUFJPVEVJTl0KICAKICBjYXQocGFzdGUwKCJcblNlbGVjdGluZyAiLCB2YXIudGVtcCwgIiBhbmQgc3RhbmRhcmRpc2luZzogIiwgdmFyLnRlbXAucmFuaywiLlxuIikpCiAgY2F0KHBhc3RlMCgiKiBjaGFuZ2luZyAiLCB2YXIudGVtcCwgIiB0byBudW1lcmljLlxuIikpCgogICMgQUVSTkFTRS5jbGluLmhkYWM5IDwtICBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIG11dGF0ZShBRVJOQVNFLmNsaW4uaGRhYzlbLHZhci50ZW1wXSA9PSByZXBsYWNlKEFFUk5BU0UuY2xpbi5oZGFjOVssdmFyLnRlbXBdLCBBRVJOQVNFLmNsaW4uaGRhYzlbLHZhci50ZW1wXT09MCwgTkEpKQoKICBBRVJOQVNFLmNsaW4uaGRhYzlbLHZhci50ZW1wXVtBRVJOQVNFLmNsaW4uaGRhYzlbLHZhci50ZW1wXT09MC4wMDAwMDBdPU5BCgogIGNhdChwYXN0ZTAoIiogc3RhbmRhcmRpc2luZyAiLCB2YXIudGVtcCwgCiAgICAgICAgICAgICAiIChtZWFuOiAiLHJvdW5kKG1lYW4oIWlzLm5hKEFFUk5BU0UuY2xpbi5oZGFjOVssdmFyLnRlbXBdKSksIGRpZ2l0cyA9IDYpLAogICAgICAgICAgICAgIiwgbiA9ICIsc3VtKCFpcy5uYShBRVJOQVNFLmNsaW4uaGRhYzlbLHZhci50ZW1wXSkpLCIpLlxuIikpCiAgCiAgQUVSTkFTRS5jbGluLmhkYWM5IDwtIEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUKICAgICAgbXV0YXRlX2F0KHZhcnModmFyLnRlbXApLCAKICAgICAgICAjIGxpc3QoWiA9IH4gKEFFUk5BU0UuY2xpbi5oZGFjOVssdmFyLnRlbXBdIC0gbWVhbihBRVJOQVNFLmNsaW4uaGRhYzlbLHZhci50ZW1wXSwgbmEucm0gPSBUUlVFKSkvc2QoQUVSTkFTRS5jbGluLmhkYWM5Wyx2YXIudGVtcF0sIG5hLnJtID0gVFJVRSkpCiAgICAgICAgbGlzdChSQU5LID0gfiBxbm9ybSgocmFuayhBRVJOQVNFLmNsaW4uaGRhYzlbLHZhci50ZW1wXSwgbmEubGFzdCA9ICJrZWVwIikgLSAwLjUpIC8gc3VtKCFpcy5uYShBRVJOQVNFLmNsaW4uaGRhYzlbLHZhci50ZW1wXSkpKSkKICAgICAgKQogICMgc3RyKFVDT1JCSU9HU0FxYyRaKQogIGNhdChwYXN0ZTAoIiogcmVuYW1pbmcgUkFOSyB0byAiLCB2YXIudGVtcC5yYW5rLCIuXG4iKSkKICBBRVJOQVNFLmNsaW4uaGRhYzlbLHZhci50ZW1wLnJhbmtdIDwtIE5VTEwKICBuYW1lcyhBRVJOQVNFLmNsaW4uaGRhYzkpW25hbWVzKEFFUk5BU0UuY2xpbi5oZGFjOSkgPT0gIlJBTksiXSA8LSB2YXIudGVtcC5yYW5rCn0KCiMgcm0odmFyLnRlbXAsIHZhci50ZW1wLnJhbmspCgpgYGAKCiMjIyBWaXN1YWxpemUgdHJhbnNmb3JtYXRpb25zCgpXZSB3aWxsIGp1c3QgdmlzdWFsaXplIHRoZXNlIHRyYW5zZm9ybWF0aW9ucy4KCmBgYHtyIEhEQUM5IHZzIEN5dG9raW5lcyBIaXN0b2dyYW1zfQpwcm90ZWluc19vZl9pbnRlcmVzdF9yYW5rX3RhcmdldCA8LSBjKCJIREFDOSIsIHByb3RlaW5zX29mX2ludGVyZXN0X3JhbmspCgpwcm90ZWluc19vZl9pbnRlcmVzdF90YXJnZXQgPC0gYygiSERBQzkiLCBwcm90ZWluc19vZl9pbnRlcmVzdCkKCmZvcihQUk9URUlOIGluIHByb3RlaW5zX29mX2ludGVyZXN0X3RhcmdldCl7CiAgY2F0KHBhc3RlMCgiUGxvdHRpbmcgcHJvdGVpbiAiLCBQUk9URUlOLCAiLlxuIikpCiAgCiAgcDEgPC0gZ2dwdWJyOjpnZ2hpc3RvZ3JhbShBRVJOQVNFLmNsaW4uaGRhYzksIFBST1RFSU4sCiAgICAgICAgICAgICAgICAgICAgIyB5ID0gIi4uY291bnQuLiIsCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiIzEyOTBEOSIsICIjREIwMDNGIiksCiAgICAgICAgICAgICAgICAgICAgYWRkID0gIm1lYW4iLAogICAgICAgICAgICAgICAgICAgICMgcnVnID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAjIGFkZC5wYXJhbXMgPSAgbGlzdChjb2xvciA9ICJibGFjayIsIGxpbmV0eXBlID0gMiksCiAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSBwYXN0ZTAoUFJPVEVJTiwgIiBwbGFxdWUgbGV2ZWxzIiksCiAgICAgICAgICAgICAgICAgICAgeGxhYiA9ICIiLAogICAgICAgICAgICAgICAgICAgIGdndGhlbWUgPSB0aGVtZV9taW5pbWFsKCkpCiAgcHJpbnQocDEpCiAgCn0KCgpmb3IoUFJPVEVJTiBpbiBwcm90ZWluc19vZl9pbnRlcmVzdF9yYW5rX3RhcmdldCl7CiAgY2F0KHBhc3RlMCgiUGxvdHRpbmcgcHJvdGVpbiAiLCBQUk9URUlOLCAiLlxuIikpCiAgCiAgcDEgPC0gZ2dwdWJyOjpnZ2hpc3RvZ3JhbShBRVJOQVNFLmNsaW4uaGRhYzksIFBST1RFSU4sCiAgICAgICAgICAgICAgICAgICAgIyB5ID0gIi4uY291bnQuLiIsCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiIzEyOTBEOSIsICIjREIwMDNGIiksCiAgICAgICAgICAgICAgICAgICAgYWRkID0gIm1lYW4iLAogICAgICAgICAgICAgICAgICAgICMgcnVnID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAjIGFkZC5wYXJhbXMgPSAgbGlzdChjb2xvciA9ICJibGFjayIsIGxpbmV0eXBlID0gMiksCiAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSBwYXN0ZTAoUFJPVEVJTiwgIiBwbGFxdWUgbGV2ZWxzIiksCiAgICAgICAgICAgICAgICAgICAgeGxhYiA9ICJpbnZlcnNlLW5vcm1hbCB0cmFuc2Zvcm1hdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgZ2d0aGVtZSA9IHRoZW1lX21pbmltYWwoKSkKICBwcmludChwMSkKICAKfQogIApgYGAKCiMjIyBDb3JyZWxhdGlvbnMKCkhlcmUgd2UgY2FsY3VsYXRlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHRhcmdldChzKSBhbmQgMjggb3RoZXIgY3l0b2tpbmVzLiBXZSB1c2UgU3BlYXJtYW4ncyB0ZXN0LCB0aHVzLCBjb3JyZWxhdGlvbnMgYSBnaXZlbiBpbiBfcmhvXy4gUGxlYXNlIG5vdGUgdGhlIGluZGljYXRpb25zIG9mIG1lYXN1cmVtZW50IG1ldGhvZHM6CgotIF9MXzogTFVNSU5FWAotIF9FXzogRUxJU0EKLSBfYV86IGFjdGl2aXR5IGFzc2F5CgpgYGB7ciBNQ1AxIHZzIEN5dG9raW5lcyBjb3JyZWxhdGlvbnN9CiMgSW5zdGFsbGF0aW9uIG9mIGdnY29ycnBsb3QoKQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmlmKCFyZXF1aXJlKGRldnRvb2xzKSkgCiAgaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImthc3NhbWJhcmEvZ2djb3JycGxvdCIpCgpsaWJyYXJ5KGdnY29ycnBsb3QpCgojIENyZWF0aW5nIG1hdHJpeCAtIGludmVyc2UtcmFuayB0cmFuc2Zvcm1hdGlvbgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnRlbXAgPC0gc3Vic2V0KEFFUk5BU0UuY2xpbi5oZGFjOSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0ID0gYyhwcm90ZWluc19vZl9pbnRlcmVzdF9yYW5rX3RhcmdldCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKIyBzdHIoQUVEQi5DRUEudGVtcCkKbWF0cml4LlJBTksgPC0gYXMubWF0cml4KHRlbXApCnJtKHRlbXApCgpjb3JyX2Jpb21hcmtlcnMucmFuayA8LSByb3VuZChjb3IobWF0cml4LlJBTkssIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiLCAjdGhlIGNvcnJlbGF0aW9uIG9yIGNvdmFyaWFuY2UgYmV0d2VlbiBlYWNoIHBhaXIgb2YgdmFyaWFibGVzIGlzIGNvbXB1dGVkIHVzaW5nIGFsbCBjb21wbGV0ZSBwYWlycyBvZiBvYnNlcnZhdGlvbnMgb24gdGhvc2UgdmFyaWFibGVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInNwZWFybWFuIiksIDMpCiMgY29ycl9iaW9tYXJrZXJzLnJhbmsKCnJlbmFtZV9wcm90ZWluc19vZl9pbnRlcmVzdF90YXJnZXQgPC0gYygiSERBQzkgKFJOQSkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIklMMiIsICJJTDQiLCAiSUw1IiwgIklMNiIsICJJTDgiLCAiSUw5IiwgIklMMTAiLCAiSUwxMiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSUwxMyAoTCkiLCAiSUwyMSAoTCkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIklORkciLCAiVE5GQSIsICJNSUYgKEwpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNQ1AxIChMKSIsICJNSVAxYSAoTCkiLCAiUkFOVEVTIChMKSIsICJNSUcgKEwpIiwgIklQMTAgKEwpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFb3RheGluMSAoTCkiLCAiVEFSQyAoTCkiLCAiUEFSQyAoTCkiLCAiTURDIChMKSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiT1BHIChMKSIsICJzSUNBTTEgKEwpIiwgIlZFR0ZBIChFKSIsICJUR0ZCIChFKSIsICJNTVAyIChhKSIsICJNTVA4IChhKSIsICJNTVA5IChhKSIpCmNvbG5hbWVzKGNvcnJfYmlvbWFya2Vycy5yYW5rKSA8LSBjKHJlbmFtZV9wcm90ZWluc19vZl9pbnRlcmVzdF90YXJnZXQpCnJvd25hbWVzKGNvcnJfYmlvbWFya2Vycy5yYW5rKSA8LSBjKHJlbmFtZV9wcm90ZWluc19vZl9pbnRlcmVzdF90YXJnZXQpCgpjb3JyX2Jpb21hcmtlcnNfcC5yYW5rIDwtIGdnY29ycnBsb3Q6OmNvcl9wbWF0KG1hdHJpeC5SQU5LLCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIiwgbWV0aG9kID0gInNwZWFybWFuIikKCiMgKysrKysrKysrKysrKysrKysrKysrKysrKysrKwojIGZsYXR0ZW5Db3JyTWF0cml4CiMgKysrKysrKysrKysrKysrKysrKysrKysrKysrKwojIGNvcm1hdCA6IG1hdHJpeCBvZiB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnRzCiMgcG1hdCA6IG1hdHJpeCBvZiB0aGUgY29ycmVsYXRpb24gcC12YWx1ZXMKZmxhdHRlbkNvcnJNYXRyaXggPC0gZnVuY3Rpb24oY29ybWF0LCBwbWF0KSB7CiAgdXQgPC0gdXBwZXIudHJpKGNvcm1hdCkKICBkYXRhLmZyYW1lKAogICAgcm93ID0gcm93bmFtZXMoY29ybWF0KVtyb3coY29ybWF0KVt1dF1dLAogICAgY29sdW1uID0gcm93bmFtZXMoY29ybWF0KVtjb2woY29ybWF0KVt1dF1dLAogICAgY29yICA9KGNvcm1hdClbdXRdLAogICAgcCA9IHBtYXRbdXRdCiAgICApCn0KCmNvcnJfYmlvbWFya2Vycy5yYW5rLmRmIDwtIGZsYXR0ZW5Db3JyTWF0cml4KGNvcnJfYmlvbWFya2Vycy5yYW5rLCBjb3JyX2Jpb21hcmtlcnNfcC5yYW5rKQoKCm5hbWVzKGNvcnJfYmlvbWFya2Vycy5yYW5rLmRmKVtuYW1lcyhjb3JyX2Jpb21hcmtlcnMucmFuay5kZikgPT0gInJvdyJdIDwtICJDeXRva2luZV9YIgpuYW1lcyhjb3JyX2Jpb21hcmtlcnMucmFuay5kZilbbmFtZXMoY29ycl9iaW9tYXJrZXJzLnJhbmsuZGYpID09ICJjb2x1bW4iXSA8LSAiQ3l0b2tpbmVZIgpuYW1lcyhjb3JyX2Jpb21hcmtlcnMucmFuay5kZilbbmFtZXMoY29ycl9iaW9tYXJrZXJzLnJhbmsuZGYpID09ICJjb3IiXSA8LSAiU3BlYXJtYW5SaG8iCgpEVDo6ZGF0YXRhYmxlKGNvcnJfYmlvbWFya2Vycy5yYW5rLmRmKQoKZndyaXRlKGNvcnJfYmlvbWFya2Vycy5yYW5rLmRmLCBmaWxlID0gcGFzdGUwKE9VVF9sb2MsICIvIixUb2RheSwiLmNvcnJlbGF0aW9uX2N5dG9raW5lcy50eHQiKSkKCmBgYAoKYGBge3IgTUNQMSB2cyBDeXRva2luZXMgaGVhdG1hcH0KIyBBZGQgY29ycmVsYXRpb24gY29lZmZpY2llbnRzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBhcmd1bWVudCBsYWIgPSBUUlVFCnAxIDwtIGdnY29ycnBsb3QoY29ycl9iaW9tYXJrZXJzLnJhbmssIAogICAgICAgICAgIG1ldGhvZCA9ICJzcXVhcmUiLCAKICAgICAgICAgICB0eXBlID0gImxvd2VyIiwKICAgICAgICAgICB0aXRsZSA9ICJDcm9zcyBiaW9tYXJrZXIgY29ycmVsYXRpb25zIiwgCiAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBUUlVFLCBsZWdlbmQudGl0bGUgPSBicXVvdGUoIlNwZWFybWFuJ3Mifml0YWxpYyhyaG8pKSwKICAgICAgICAgICBnZ3RoZW1lID0gZ2dwbG90Mjo6dGhlbWVfbWluaW1hbCwgb3V0bGluZS5jb2xvciA9ICIjRkZGRkZGIiwKICAgICAgICAgICBzaG93LmRpYWcgPSBUUlVFLAogICAgICAgICAgIGhjLm9yZGVyID0gRkFMU0UsIAogICAgICAgICAgIGxhYiA9IEZBTFNFLAogICAgICAgICAgIGRpZ2l0cyA9IDMsCiAgICAgICAgICAgdGwuY2V4ID0gMTYsCiAgICAgICAgICAgIyB4bGFiID0gYygiTUNQMSIpLAogICAgICAgICAgICMgcC5tYXQgPSBjb3JyX2Jpb21hcmtlcnNfcC5yYW5rLCBzaWcubGV2ZWwgPSAwLjA1LAogICAgICAgICAgIGNvbG9ycyA9IGMoIiMxMjkwRDkiLCAiI0ZGRkZGRiIsICIjRTU1NzM4IikpCnAxCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi5jb3JyZWxhdGlvbl9jeXRva2luZXMucG5nIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsIFRvZGF5LCAiLmNvcnJlbGF0aW9uX2N5dG9raW5lcy5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKcm0ocDEpCgpgYGAKCldoaWxlIHZpc3VhbGx5IGF0dHJhY3RpdmUgd2UgYXJlIG5vdCBuZWNlc3NhcmlseSBpbnRlcmVzdGVkIGluIHRoZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiBhbGwgdGhlIGN5dG9raW5lcywgcmF0aGVyIG9mIHRhcmdldChzKWAgd2l0aCBvdGhlciBjeXRva2luZXMgb25seS4KCmBgYHtyIE1DUDEgdnMgQ3l0b2tpbmVzIGJhcnBsb3R9CnRlbXAgPC0gc3Vic2V0KGNvcnJfYmlvbWFya2Vycy5yYW5rLmRmLCBDeXRva2luZV9YID09ICJIREFDOSAoUk5BKSIgKQp0ZW1wJHBfbG9nMTAgPC0gLWxvZzEwKHRlbXAkcCkKcF90aHJlc2hvbGQgPC0gLWxvZzEwKDAuMDUvbnJvdyh0ZW1wKSkKcF90aHJlc2hvbGQKcDEgPC0gZ2diYXJwbG90KHRlbXAsIHggPSAiQ3l0b2tpbmVZIiwgeSA9ICJTcGVhcm1hblJobyIsCiAgICAgICAgICBmaWxsID0gIkN5dG9raW5lWSIsICAgICAgICAgICAgICAgIyBjaGFuZ2UgZmlsbCBjb2xvciBieSBjeWwKICAgICAgICAgICMgY29sb3IgPSAid2hpdGUiLCAgICAgICAgICAgICMgU2V0IGJhciBib3JkZXIgY29sb3JzIHRvIHdoaXRlCiAgICAgICAgICBwYWxldHRlID0gdWl0aG9mX2NvbG9yLCAgICAgICAgICAgICMgamNvIGpvdXJuYWwgY29sb3IgcGFsZXR0LiBzZWUgP2dncGFyCiAgICAgICAgICB4bGFiID0gIkN5dG9raW5lIiwKICAgICAgICAgIHlsYWIgPSBleHByZXNzaW9uKCJTcGVhcm1hbidzIn5pdGFsaWMocmhvKSksCiAgICAgICAgICBzb3J0LnZhbCA9ICJkZXNjIiwgICAgICAgICAgIyBTb3J0IHRoZSB2YWx1ZSBpbiBkc2NlbmRpbmcgb3JkZXIKICAgICAgICAgIHNvcnQuYnkuZ3JvdXBzID0gRkFMU0UsICAgICAjIERvbid0IHNvcnQgaW5zaWRlIGVhY2ggZ3JvdXAKICAgICAgICAgIHgudGV4dC5hbmdsZSA9IDQ1LCAjIFJvdGF0ZSB2ZXJ0aWNhbGx5IHggYXhpcyB0ZXh0cwogICAgICAgICAgY2V4ID0gMS4yNQogICAgICAgICAgKQpnZ3BhcihwMSwgbGVnZW5kID0gImJvdHRvbSIsIAogICAgICBsZWdlbmQudGl0bGUgPSAiIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgCgpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIl92c19DeXRva2luZXMucG5nIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCJfdnNfQ3l0b2tpbmVzLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCnJtKHAxKQoKCmBgYAoKQW5vdGhlciB2ZXJzaW9uIC0gcHJvYmFibHkgbm90IGdvb2QuIApgYGB7ciBNQ1AxIHZzIEN5dG9raW5lcyBkb3RjaGFydH0KdGVtcCA8LSBzdWJzZXQoY29ycl9iaW9tYXJrZXJzLnJhbmsuZGYsIEN5dG9raW5lX1ggPT0gIkhEQUM5IChSTkEpIiApCnRlbXAkcF9sb2cxMCA8LSAtbG9nMTAodGVtcCRwKQpwX3RocmVzaG9sZCA8LSAtbG9nMTAoMC4wNS9ucm93KHRlbXApKQpwX3RocmVzaG9sZApwMSA8LSBnZ2RvdGNoYXJ0KHRlbXAsIHggPSAiQ3l0b2tpbmVZIiwgeSA9ICJwX2xvZzEwIiwKICAgICAgICAgICBjb2xvciA9ICJDeXRva2luZVkiLCAjZmlsbCA9ICJDeXRva2luZVkiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQ29sb3IgYnkgZ3JvdXBzCiAgICAgICAgICAgcGFsZXR0ZSA9IHVpdGhvZl9jb2xvciwgIyBDdXN0b20gY29sb3IgcGFsZXR0ZQogICAgICAgICAgIHhsYWIgPSAiQ3l0b2tpbmUiLAogICAgICAgICAgIHlsYWIgPSBleHByZXNzaW9uKGxvZ1sxMF1+Iigifml0YWxpYyhwKX4iKS12YWx1ZSIpLAogICAgICAgICAgICMgeWxpbSA9IGMoMCwgOSksCiAgICAgICAgICAgc29ydGluZyA9ICJkZXNjZW5kaW5nIiwgICAgICAgICAgICAgICAgICAgICAgICMgU29ydCB2YWx1ZSBpbiBkZXNjZW5kaW5nIG9yZGVyCiAgICAgICAgICAgYWRkID0gInNlZ21lbnRzIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQWRkIHNlZ21lbnRzIGZyb20geSA9IDAgdG8gZG90cwogICAgICAgICAgIHJvdGF0ZSA9IEZBTFNFLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSb3RhdGUgdmVydGljYWxseQogICAgICAgICAgICMgZ3JvdXAgPSAiQ3l0b2tpbmVZIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgT3JkZXIgYnkgZ3JvdXBzCiAgICAgICAgICAgZG90LnNpemUgPSAxNiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIExhcmdlIGRvdCBzaXplCiAgICAgICAgICAgbGFiZWwgPSByb3VuZCh0ZW1wJFNwZWFybWFuUmhvLCBkaWdpdHMgPSAzKSwgICAgICAgICAgICAgICAgICAgICAgICAjIEFkZCBtcGcgdmFsdWVzIGFzIGRvdCBsYWJlbHMKICAgICAgICAgICBmb250LmxhYmVsID0gbGlzdChjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAxMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3QgPSAwLjUpICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICkKZ2dwYXIocDEsIGxlZ2VuZCA9ICIiLCAKICAgICAgbGVnZW5kLnRpdGxlID0gIiIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpCgpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLmhkYWM5LiIsVFJBSVRfT0ZfSU5URVJFU1QsIl92c19DeXRva2luZXMuZG90Y2hhcnQucG5nIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS4iLFRSQUlUX09GX0lOVEVSRVNULCJfdnNfQ3l0b2tpbmVzLmRvdGNoYXJ0LnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpybSh0ZW1wLCBwMSkKCmBgYAoKCiMjIEhEQUM5IHZzLiBjeXRva2luZXMgcGxhcXVlIGxldmVscwoKIyMjIE1vZGVsIDEKCkluIHRoaXMgbW9kZWwgd2UgY29ycmVjdCBmb3IgX0FnZV8sIF9HZW5kZXJfLCBhbmQgX3llYXIgb2Ygc3VyZ2VyeV8uCgpIZXJlIHdlIHVzZSB0aGUgaW52ZXJzZS1yYW5rIG5vcm1hbGl6ZWQgZGF0YSAtIHZpc3VhbGx5IHRoaXMgaXMgbW9yZSBub3JtYWxseSBkaXN0cmlidXRlZC4KCkFuYWx5c2lzIG9mIHBsYXF1ZSBjeXRva2luZXMgdHJhaXRzIGFzIGEgZnVuY3Rpb24gb2YgcGxhcXVlIHRhcmdldChzKSBsZXZlbHMuCgpgYGB7ciBDcm9zc1NlYzogQ3l0b2tpbmVzIC0gbGluZWFyIHJlZ3Jlc3Npb24gTU9ERUwxIFJBTkssIHBhZ2VkLnByaW50PVRSVUV9CgpHTE0ucmVzdWx0cyA8LSBkYXRhLmZyYW1lKG1hdHJpeChOQSwgbmNvbCA9IDE1LCBucm93ID0gMCkpCmNhdCgiUnVubmluZyBsaW5lYXIgcmVncmVzc2lvbi4uLlxuIikKZm9yIChwcm90ZWluIGluIDE6bGVuZ3RoKFRSQUlUUy5UQVJHRVQuUkFOSykpIHsKICBQUk9URUlOID0gVFJBSVRTLlRBUkdFVC5SQU5LW3Byb3RlaW5dCiAgY2F0KHBhc3RlMCgiXG5BbmFseXNpcyBvZiAiLFBST1RFSU4sIi5cbiIpKQogIGZvciAodHJhaXQgaW4gMTpsZW5ndGgocHJvdGVpbnNfb2ZfaW50ZXJlc3RfcmFuaykpIHsKICAgIFRSQUlUID0gcHJvdGVpbnNfb2ZfaW50ZXJlc3RfcmFua1t0cmFpdF0KICAgIGNhdChwYXN0ZTAoIlxuLSBwcm9jZXNzaW5nICIsVFJBSVQsIlxuXG4iKSkKICAgIGN1cnJlbnRERiA8LSBhcy5kYXRhLmZyYW1lKEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUKICAgICAgZHBseXI6OnNlbGVjdCguLCBQUk9URUlOLCBUUkFJVCwgQ09WQVJJQVRFU19NMSkgJT4lCiAgICAgIGZpbHRlcihjb21wbGV0ZS5jYXNlcyguKSkpICU+JQogICAgICBmaWx0ZXJfaWYofmlzLm51bWVyaWMoLiksIGFsbF92YXJzKCFpcy5pbmZpbml0ZSguKSkpCiAgICAjIGZvciBkZWJ1ZwogICAgIyBwcmludChEVDo6ZGF0YXRhYmxlKGN1cnJlbnRERikpCiAgICAjIHByaW50KG5yb3coY3VycmVudERGKSkKICAgICMgcHJpbnQoc3RyKGN1cnJlbnRERikpCiAgICAjIyMgdW5pdmFyaWF0ZQogICAgIyBmaXQgPC0gbG0oY3VycmVudERGWyxQUk9URUlOXSB+IGN1cnJlbnRERlssVFJBSVRdICsgQWdlICsgR2VuZGVyICsgT1JkYXRlX3llYXIsIGRhdGEgPSBjdXJyZW50REYpCiAgICBmaXQgPC0gbG0oY3VycmVudERGWyxQUk9URUlOXSB+IGN1cnJlbnRERlssVFJBSVRdICsgQWdlICsgR2VuZGVyICsgT1JkYXRlX2Vwb2NoLCBkYXRhID0gY3VycmVudERGKQogICAgbW9kZWxfc3RlcCA8LSBzdGVwQUlDKGZpdCwgZGlyZWN0aW9uID0gImJvdGgiLCB0cmFjZSA9IEZBTFNFKQogICAgcHJpbnQobW9kZWxfc3RlcCkKICAgIHByaW50KHN1bW1hcnkoZml0KSkKCiAgICBHTE0ucmVzdWx0cy5URU1QIDwtIGRhdGEuZnJhbWUobWF0cml4KE5BLCBuY29sID0gMTUsIG5yb3cgPSAwKSkKICAgIEdMTS5yZXN1bHRzLlRFTVBbMSxdID0gR0xNLkNPTihmaXQsICJBRURCLkNFQSIsIFBST1RFSU4sIFRSQUlULCB2ZXJib3NlID0gVFJVRSkKICAgIEdMTS5yZXN1bHRzID0gcmJpbmQoR0xNLnJlc3VsdHMsIEdMTS5yZXN1bHRzLlRFTVApCiAgfQp9CmNhdCgiRWRpdCB0aGUgY29sdW1uIG5hbWVzLi4uXG4iKQpjb2xuYW1lcyhHTE0ucmVzdWx0cykgPSBjKCJEYXRhc2V0IiwgIlByZWRpY3RvciIsICJUcmFpdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIkJldGEiLCAicy5lLm0uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiT1IiLCAibG93OTVDSSIsICJ1cDk1Q0kiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJULXZhbHVlIiwgIlAtdmFsdWUiLCAicl4yIiwgInJeMl9hZGoiLCAiTiIsICJNb2RlbF9OIiwgIlBlcmNfTWlzcyIpCgpjYXQoIkNvcnJlY3QgdGhlIHZhcmlhYmxlIHR5cGVzLi4uXG4iKQpHTE0ucmVzdWx0cyRCZXRhIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkQmV0YSkKR0xNLnJlc3VsdHMkcy5lLm0uIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkcy5lLm0uKQpHTE0ucmVzdWx0cyRPUiA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJE9SKQpHTE0ucmVzdWx0cyRsb3c5NUNJIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkbG93OTVDSSkKR0xNLnJlc3VsdHMkdXA5NUNJIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkdXA5NUNJKQpHTE0ucmVzdWx0cyRgVC12YWx1ZWAgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRgVC12YWx1ZWApCkdMTS5yZXN1bHRzJGBQLXZhbHVlYCA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJGBQLXZhbHVlYCkKR0xNLnJlc3VsdHMkYHJeMmAgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRgcl4yYCkKR0xNLnJlc3VsdHMkYHJeMl9hZGpgIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkYHJeMl9hZGpgKQpHTE0ucmVzdWx0cyRgTmAgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRgTmApCkdMTS5yZXN1bHRzJGBNb2RlbF9OYCA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJGBNb2RlbF9OYCkKR0xNLnJlc3VsdHMkYFBlcmNfTWlzc2AgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRgUGVyY19NaXNzYCkKCmBgYAoKYGBge3IgQ3Jvc3NTZWM6IEN5dG9raW5lcyAtIGxpbmVhciByZWdyZXNzaW9uIE1PREVMMSBSQU5LIFdyaXRpbmd9CkRUOjpkYXRhdGFibGUoR0xNLnJlc3VsdHMpCgojIFNhdmUgdGhlIGRhdGEKY2F0KCJXcml0aW5nIHJlc3VsdHMgdG8gRXhjZWwtZmlsZS4uLlxuIikKIyMjIFVuaXZhcmlhdGUKbGlicmFyeShvcGVueGxzeCkKd3JpdGUueGxzeChHTE0ucmVzdWx0cywKICAgICAgICAgICBmaWxlID0gcGFzdGUwKE9VVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5oZGFjOS5Db24uVW5pLiIsVFJBSVRfT0ZfSU5URVJFU1QsIl9QbGFxdWUuQ3l0b2tpbmVzX1BsYXF1ZXMuUkFOSy5NT0RFTDEueGxzeCIpLAogICAgICAgICAgIHJvd05tZXMgPSBGQUxTRSwgY29sTmFtZXMgPSBUUlVFLCBzaGVldE5hbWUgPSAiQ29uLlVuaS5QbGFxdWVQaGVubyIpCiMgUmVtb3ZpbmcgaW50ZXJtZWRpYXRlcwpjYXQoIlJlbW92aW5nIGludGVybWVkaWF0ZSBmaWxlcy4uLlxuIikKcm0oVFJBSVQsIHRyYWl0LCBjdXJyZW50REYsIEdMTS5yZXN1bHRzLCBHTE0ucmVzdWx0cy5URU1QLCBmaXQsIG1vZGVsX3N0ZXApCgoKYGBgCgoKCiMjIyBNb2RlbCAyCgpJbiB0aGlzIG1vZGVsIHdlIGNvcnJlY3QgZm9yIF9BZ2VfLCBfR2VuZGVyXywgX3llYXIgb2Ygc3VyZ2VyeV8sIF9IeXBlcnRlbnNpb24gc3RhdHVzXywgX0RpYWJldGVzIHN0YXR1c18sIF9jdXJyZW50IHNtb2tlciBzdGF0dXNfLCBfbGlwaWQtbG93ZXJpbmcgZHJ1Z3MgKExMRHMpXywgX2FudGlwbGF0ZWxldCBtZWRpY2F0aW9uXywgX2VHRlIgKE1EUkQpXywgX0JNSV8sIF9NZWRIeF9DVkRfIChjb21iaW5hdGlvbiBvZiBfQ0FEIGhpc3RvcnlfLCBfc3Ryb2tlIGhpc3RvcnlfLCBhbmQgX3BlcmlwaGVyYWwgaW50ZXJ2ZW50aW9uc18pLCBhbmQgX3N0ZW5vc2lzXy4KCkhlcmUgd2UgdXNlIHRoZSBpbnZlcnNlLXJhbmsgbm9ybWFsaXplZCBkYXRhIC0gdmlzdWFsbHkgdGhpcyBpcyBtb3JlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLgoKQW5hbHlzaXMgb2YgcGxhcXVlIGN5dG9raW5lcyBhcyBhIGZ1bmN0aW9uIG9mIHBsYXF1ZSB0YXJnZXQocykgbGV2ZWxzLgoKYGBge3IgQ3Jvc3NTZWM6IEN5dG9raW5lcyAtIGxpbmVhciByZWdyZXNzaW9uIE1PREVMMiBSQU5LLCBwYWdlZC5wcmludD1UUlVFfQoKR0xNLnJlc3VsdHMgPC0gZGF0YS5mcmFtZShtYXRyaXgoTkEsIG5jb2wgPSAxNSwgbnJvdyA9IDApKQpjYXQoIlJ1bm5pbmcgbGluZWFyIHJlZ3Jlc3Npb24uLi5cbiIpCmZvciAocHJvdGVpbiBpbiAxOmxlbmd0aChUUkFJVFMuVEFSR0VULlJBTkspKSB7CiAgUFJPVEVJTiA9IFRSQUlUUy5UQVJHRVQuUkFOS1twcm90ZWluXQogIGNhdChwYXN0ZTAoIlxuQW5hbHlzaXMgb2YgIixQUk9URUlOLCIuXG4iKSkKICBmb3IgKHRyYWl0IGluIDE6bGVuZ3RoKHByb3RlaW5zX29mX2ludGVyZXN0X3JhbmspKSB7CiAgICBUUkFJVCA9IHByb3RlaW5zX29mX2ludGVyZXN0X3JhbmtbdHJhaXRdCiAgICBjYXQocGFzdGUwKCJcbi0gcHJvY2Vzc2luZyAiLFRSQUlULCJcblxuIikpCiAgICBjdXJyZW50REYgPC0gYXMuZGF0YS5mcmFtZShBRVJOQVNFLmNsaW4uaGRhYzkgJT4lCiAgICAgIGRwbHlyOjpzZWxlY3QoLiwgUFJPVEVJTiwgVFJBSVQsIENPVkFSSUFURVNfTTIpICU+JQogICAgICBmaWx0ZXIoY29tcGxldGUuY2FzZXMoLikpKSAlPiUKICAgICAgZmlsdGVyX2lmKH5pcy5udW1lcmljKC4pLCBhbGxfdmFycyghaXMuaW5maW5pdGUoLikpKQogICAgIyBmb3IgZGVidWcKICAgICMgcHJpbnQoRFQ6OmRhdGF0YWJsZShjdXJyZW50REYpKQogICAgIyBwcmludChucm93KGN1cnJlbnRERikpCiAgICAjIHByaW50KHN0cihjdXJyZW50REYpKQogICAgIyMjIHVuaXZhcmlhdGUKICAgICMgZml0IDwtIGxtKGN1cnJlbnRERlssUFJPVEVJTl0gfiBjdXJyZW50REZbLFRSQUlUXSArIEFnZSArIEdlbmRlciArIE9SZGF0ZV95ZWFyICsgCiAgICAjICAgICAgICAgICAgIEh5cGVydGVuc2lvbi5jb21wb3NpdGUgKyBEaWFiZXRlc1N0YXR1cyArIFNtb2tlclN0YXR1cyArIAogICAgIyAgICAgICAgICAgICBNZWQuU3RhdGluLkxMRCArIE1lZC5hbGwuYW50aXBsYXRlbGV0ICsgR0ZSX01EUkQgKyBCTUkgKyAKICAgICMgICAgICAgICAgICAgTWVkSHhfQ1ZEICsgc3Rlbm9zZSwgCiAgICAjICAgICAgICAgICBkYXRhID0gY3VycmVudERGKQogICAgZml0IDwtIGxtKGN1cnJlbnRERlssUFJPVEVJTl0gfiBjdXJyZW50REZbLFRSQUlUXSArIEFnZSArIEdlbmRlciArIE9SZGF0ZV9lcG9jaCArIAogICAgICAgICAgICAgICAgSHlwZXJ0ZW5zaW9uLmNvbXBvc2l0ZSArIERpYWJldGVzU3RhdHVzICsgU21va2VyU3RhdHVzICsgCiAgICAgICAgICAgICAgICBNZWQuU3RhdGluLkxMRCArIE1lZC5hbGwuYW50aXBsYXRlbGV0ICsgR0ZSX01EUkQgKyBCTUkgKyAKICAgICAgICAgICAgICAgIE1lZEh4X0NWRCArIHN0ZW5vc2UsIAogICAgICAgICAgICAgIGRhdGEgPSBjdXJyZW50REYpCiAgICBtb2RlbF9zdGVwIDwtIHN0ZXBBSUMoZml0LCBkaXJlY3Rpb24gPSAiYm90aCIsIHRyYWNlID0gRkFMU0UpCiAgICBwcmludChtb2RlbF9zdGVwKQogICAgcHJpbnQoc3VtbWFyeShmaXQpKQogICAgCiAgICBHTE0ucmVzdWx0cy5URU1QIDwtIGRhdGEuZnJhbWUobWF0cml4KE5BLCBuY29sID0gMTUsIG5yb3cgPSAwKSkKICAgIEdMTS5yZXN1bHRzLlRFTVBbMSxdID0gR0xNLkNPTihmaXQsICJBRURCLkNFQSIsIFBST1RFSU4sIFRSQUlULCB2ZXJib3NlID0gVFJVRSkKICAgIEdMTS5yZXN1bHRzID0gcmJpbmQoR0xNLnJlc3VsdHMsIEdMTS5yZXN1bHRzLlRFTVApCiAgfQp9CmNhdCgiRWRpdCB0aGUgY29sdW1uIG5hbWVzLi4uXG4iKQpjb2xuYW1lcyhHTE0ucmVzdWx0cykgPSBjKCJEYXRhc2V0IiwgIlByZWRpY3RvciIsICJUcmFpdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIkJldGEiLCAicy5lLm0uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiT1IiLCAibG93OTVDSSIsICJ1cDk1Q0kiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJULXZhbHVlIiwgIlAtdmFsdWUiLCAicl4yIiwgInJeMl9hZGoiLCAiTiIsICJNb2RlbF9OIiwgIlBlcmNfTWlzcyIpCgpjYXQoIkNvcnJlY3QgdGhlIHZhcmlhYmxlIHR5cGVzLi4uXG4iKQpHTE0ucmVzdWx0cyRCZXRhIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkQmV0YSkKR0xNLnJlc3VsdHMkcy5lLm0uIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkcy5lLm0uKQpHTE0ucmVzdWx0cyRPUiA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJE9SKQpHTE0ucmVzdWx0cyRsb3c5NUNJIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkbG93OTVDSSkKR0xNLnJlc3VsdHMkdXA5NUNJIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkdXA5NUNJKQpHTE0ucmVzdWx0cyRgVC12YWx1ZWAgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRgVC12YWx1ZWApCkdMTS5yZXN1bHRzJGBQLXZhbHVlYCA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJGBQLXZhbHVlYCkKR0xNLnJlc3VsdHMkYHJeMmAgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRgcl4yYCkKR0xNLnJlc3VsdHMkYHJeMl9hZGpgIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkYHJeMl9hZGpgKQpHTE0ucmVzdWx0cyRgTmAgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRgTmApCkdMTS5yZXN1bHRzJGBNb2RlbF9OYCA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJGBNb2RlbF9OYCkKR0xNLnJlc3VsdHMkYFBlcmNfTWlzc2AgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRgUGVyY19NaXNzYCkKCmBgYAoKYGBge3IgQ3Jvc3NTZWM6IEN5dG9raW5lcyAtIGxpbmVhciByZWdyZXNzaW9uIE1PREVMMiBSQU5LLCB3cml0aW5nfQpEVDo6ZGF0YXRhYmxlKEdMTS5yZXN1bHRzKQoKIyBTYXZlIHRoZSBkYXRhCmNhdCgiV3JpdGluZyByZXN1bHRzIHRvIEV4Y2VsLWZpbGUuLi5cbiIpCiMjIyBVbml2YXJpYXRlCmxpYnJhcnkob3Blbnhsc3gpCndyaXRlLnhsc3goR0xNLnJlc3VsdHMsCiAgICAgICAgICAgZmlsZSA9IHBhc3RlMChPVVRfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uaGRhYzkuQ29uLk11bHRpLiIsVFJBSVRfT0ZfSU5URVJFU1QsIl9QbGFxdWUuQ3l0b2tpbmVzX1BsYXF1ZXMuUkFOSy5NT0RFTDIueGxzeCIpLAogICAgICAgICAgIHJvd05hbWVzID0gRkFMU0UsIGNvbE5hbWVzID0gVFJVRSwgc2hlZXROYW1lID0gIkNvbi5NdWx0aS5QbGFxdWVQaGVubyIpCiMgUmVtb3ZpbmcgaW50ZXJtZWRpYXRlcwpjYXQoIlJlbW92aW5nIGludGVybWVkaWF0ZSBmaWxlcy4uLlxuIikKcm0oVFJBSVQsIHRyYWl0LCBjdXJyZW50REYsIEdMTS5yZXN1bHRzLCBHTE0ucmVzdWx0cy5URU1QLCBmaXQsIG1vZGVsX3N0ZXApCgoKYGBgCgojIyBIREFDOSBsZXZlbHMgdnMuIHZ1bG5lcmFiaWxpdHkgaW5kZXgKCgpIZXJlIHdlIHBsb3QgdGhlIGxldmVscyBvZiBpbnZlcnNlLXJhbmsgbm9ybWFsIHRyYW5zZm9ybWVkIHRhcmdldChzKSBwbGFxdWUgbGV2ZWxzIGZyb20gZXhwZXJpbWVudCAxIGFuZCAyIHRvIHRoZSBgUGxhcXVlIHZ1bG5lcmFiaWxpdHkgaW5kZXhgLiAKCmBgYHtyIEZpeCBPUnllYXJHcm91cCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShzamxhYmVsbGVkKQoKQUVSTkFTRS5jbGluLmhkYWM5JHllYXJ0ZW1wIDwtIGFzLm51bWVyaWMoeWVhcihBRVJOQVNFLmNsaW4uaGRhYzkkZGF0ZW9rKSkKCmF0dGFjaChBRVJOQVNFLmNsaW4uaGRhYzkpCgpBRVJOQVNFLmNsaW4uaGRhYzlbLCJPUnllYXJHcm91cCJdIDwtIE5BCkFFUk5BU0UuY2xpbi5oZGFjOSRPUnllYXJHcm91cFt5ZWFydGVtcCA8PSAyMDA3XSA8LSAiPCAyMDA3IgpBRVJOQVNFLmNsaW4uaGRhYzkkT1J5ZWFyR3JvdXBbeWVhcnRlbXAgPiAyMDA3XSA8LSAiPiAyMDA3IgpkZXRhY2goQUVSTkFTRS5jbGluLmhkYWM5KQoKdGFibGUoQUVSTkFTRS5jbGluLmhkYWM5JE9SeWVhckdyb3VwLCBBRVJOQVNFLmNsaW4uaGRhYzkkT1JkYXRlX3llYXIpCmBgYAoKIyMjIFZpc3VhbGlzYXRpb25zCgpgYGB7ciBwZXIgUGxhcXVlVnVsbmVyYWJpbGl0eUluZGV4fQojIEdsb2JhbCB0ZXN0CmNvbXBhcmVfbWVhbnMoSERBQzkgfiBQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCwgIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpwMSA8LSBnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzksIAogICAgICAgICAgICAgICAgICB4ID0gIlBsYXF1ZV9WdWxuZXJhYmlsaXR5X0luZGV4IiwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIlBsYXF1ZSB2dWxuZXJhYmlsaXR5IGluZGV4IiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKVxuIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiLCAKICAgICAgICAgICAgICAgICAgYWRkLnBhcmFtcyA9IGxpc3Qoc2l6ZSA9IDIsIGppdHRlciA9IDAuMikpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwgPSAicC5mb3JtYXQiLCAgbWV0aG9kID0gImtydXNrYWwudGVzdCIpICsKICBmb250KCJ4bGFiIiwgc2l6ZSA9IDE3KSArCiAgZm9udCgieWxhYiIsIHNpemUgPSAxNykgKwogIGZvbnQoInh5LnRleHQiLCBzaXplID0gMTYpICsKICBmb250KCJsZWdlbmQudGl0bGUiLCBmYWNlID0gImJvbGQiKSAKICAKZ2dwYXIocDEsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiUGxhcXVlIHZ1bG5lcmFiaWxpdHkgaW5kZXgiKQoKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsIFRvZGF5LCAiLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuUGxhcXVlVnVsbmVyYWJpbGl0eUluZGV4LnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCmBgYAoKYGBge3IgfQojIEdsb2JhbCB0ZXN0CmNvbXBhcmVfbWVhbnMoSERBQzkgfiBQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCwgIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQoKcDEgPC0gZ2dwdWJyOjpnZ2JhcnBsb3QoQUVSTkFTRS5jbGluLmhkYWM5LCAKICAgICAgICAgICAgICAgICAgeCA9ICJQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCIsCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJQbGFxdWUgdnVsbmVyYWJpbGl0eSBpbmRleCIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbilcbiIsCiAgICAgICAgICAgICAgICAgIGNvbCA9ICJQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCIsCiAgICAgICAgICAgICAgICAgIGZpbGwgPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJtZWRpYW5faXFyIiwgZXJyb3IucGxvdCA9ICJ1cHBlcl9lcnJvcmJhciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwgPSAicC5mb3JtYXQiLCAgbWV0aG9kID0gImtydXNrYWwudGVzdCIsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsLnggPSAxLCBsYWJlbC55ID0gNTApICsKICBmb250KCJ4bGFiIiwgc2l6ZSA9IDE3KSArCiAgZm9udCgieWxhYiIsIHNpemUgPSAxNykgKwogIGZvbnQoInh5LnRleHQiLCBzaXplID0gMTYpICsKICBmb250KCJsZWdlbmQudGl0bGUiLCBmYWNlID0gImJvbGQiKSAKICAKZ2dwYXIocDEsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiUGxhcXVlIHZ1bG5lcmFiaWxpdHkgaW5kZXgiLCB5bGltID0gYygwLCA1NSkpCgpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIiwgVG9kYXksICIuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5QbGFxdWVWdWxuZXJhYmlsaXR5SW5kZXguQmFyUGxvdC5tZWRpYW5faXFyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhEQUM5IH4gUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgsICBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5LCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKYGBgCgpgYGB7cn0KcDEgPC0gZ2dwdWJyOjpnZ2JhcnBsb3QoQUVSTkFTRS5jbGluLmhkYWM5LCAKICAgICAgICAgICAgICAgICAgeCA9ICJQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCIsCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJQbGFxdWUgdnVsbmVyYWJpbGl0eSBpbmRleCIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbilcbiIsCiAgICAgICAgICAgICAgICAgIGNvbCA9ICJQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCIsCiAgICAgICAgICAgICAgICAgIGZpbGwgPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJtZWFuX3NlIiwgZXJyb3IucGxvdCA9ICJ1cHBlcl9lcnJvcmJhciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwgPSAicC5mb3JtYXQiLCAgbWV0aG9kID0gImtydXNrYWwudGVzdCIsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsLnggPSAxLCBsYWJlbC55ID0gNTApICsKICBmb250KCJ4bGFiIiwgc2l6ZSA9IDE3KSArCiAgZm9udCgieWxhYiIsIHNpemUgPSAxNykgKwogIGZvbnQoInh5LnRleHQiLCBzaXplID0gMTYpICsKICBmb250KCJsZWdlbmQudGl0bGUiLCBmYWNlID0gImJvbGQiKSAKICAKZ2dwYXIocDEsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiUGxhcXVlIHZ1bG5lcmFiaWxpdHkgaW5kZXgiLCB5bGltID0gYygwLCA1NSkpCgpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIiwgVG9kYXksICIuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5QbGFxdWVWdWxuZXJhYmlsaXR5SW5kZXguQmFyUGxvdC5tZWFuc19zZS5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKYGBgCgoKYGBge3J9CmNvbXBhcmVfbWVhbnMoSERBQzkgfiBQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCwgIGRhdGEgPSBzdWJzZXQoQUVSTkFTRS5jbGluLmhkYWM5LCBIREFDOSA8MTAwKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCgpwMSA8LSBnZ3B1YnI6OmdnYm94cGxvdChzdWJzZXQoQUVSTkFTRS5jbGluLmhkYWM5LCBIREFDOSA8MTAwKSAsIAogICAgICAgICAgICAgICAgICB4ID0gIlBsYXF1ZV9WdWxuZXJhYmlsaXR5X0luZGV4IiwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIlBsYXF1ZSB2dWxuZXJhYmlsaXR5IGluZGV4IiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKVxub3V0bGllcnMgYWJvdmUgMTAwIGFyZSByZW1vdmVkIiwKICAgICAgICAgICAgICAgICAgY29sID0gIlBsYXF1ZV9WdWxuZXJhYmlsaXR5X0luZGV4IiwKICAgICAgICAgICAgICAgICAgZmlsbCA9ICJQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImJveHBsb3QiLCBlcnJvci5wbG90ID0gImNyb3NzYmFyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLmZvcm1hdCIsICBtZXRob2QgPSAia3J1c2thbC50ZXN0IiwKICAgICAgICAgICAgICAgICAgICAgbGFiZWwueCA9IDEsIGxhYmVsLnkgPSA1MCkgKwogIGZvbnQoInhsYWIiLCBzaXplID0gMTcpICsKICBmb250KCJ5bGFiIiwgc2l6ZSA9IDE3KSArCiAgZm9udCgieHkudGV4dCIsIHNpemUgPSAxNikgKwogIGZvbnQoImxlZ2VuZC50aXRsZSIsIGZhY2UgPSAiYm9sZCIpIAogIApnZ3BhcihwMSwgbGVnZW5kID0gImJvdHRvbSIsIGxlZ2VuZC50aXRsZSA9ICJQbGFxdWUgdnVsbmVyYWJpbGl0eSBpbmRleCIpCgpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIiwgVG9kYXksICIuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5QbGFxdWVWdWxuZXJhYmlsaXR5SW5kZXguQm94cGxvdC5vdXRsaWVyX2Fib3ZlXzEwMF9yZW1vdmVkLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpgYGAKCmBgYHtyfQpjb21wYXJlX21lYW5zKEhEQUM5IH4gUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgsICBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5LCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDEgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5LCAKICAgICAgICAgICAgICAgICAgeCA9ICJQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCIsCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgZmFjZXQuYnkgPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiLAogICAgICAgICAgICAgICAgICB4bGFiID0gIlBsYXF1ZSB2dWxuZXJhYmlsaXR5IGluZGV4IiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKVxuIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiLCAKICAgICAgICAgICAgICAgICAgYWRkLnBhcmFtcyA9IGxpc3Qoc2l6ZSA9IDIsIGppdHRlciA9IDAuMikpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwgPSAicC5mb3JtYXQiLCAgbWV0aG9kID0gImtydXNrYWwudGVzdCIpICsKICBmb250KCJ4bGFiIiwgc2l6ZSA9IDE3KSArCiAgZm9udCgieWxhYiIsIHNpemUgPSAxNykgKwogIGZvbnQoInh5LnRleHQiLCBzaXplID0gMTYpICsKICBmb250KCJsZWdlbmQudGl0bGUiLCBmYWNlID0gImJvbGQiKSAKICAKZ2dwYXIocDEsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiUGxhcXVlIHZ1bG5lcmFiaWxpdHkgaW5kZXgiKQoKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsIFRvZGF5LCAiLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuRmFjZXRCeVBsYXF1ZVZ1bG5lcmFiaWxpdHlJbmRleC5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQpgYGAKCgpgYGB7cn0KY29tcGFyZV9tZWFucyhIREFDOSB+IFBsYXF1ZV9WdWxuZXJhYmlsaXR5X0luZGV4LCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5LCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDIgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5LCAKICAgICAgICAgICAgICAgICAgeCA9ICJQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCIsCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJQbGFxdWUgdnVsbmVyYWJpbGl0eSBpbmRleCBieSBnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pXG4iLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiI0Q1MjY3QiIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IEdlbmRlciksIGxhYmVsID0gInAuZm9ybWF0IiwgIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3BhcihwMiwgbGVnZW5kID0gImJvdHRvbSIsIGxlZ2VuZC50aXRsZSA9ICJQbGFxdWUgdnVsbmVyYWJpbGl0eSBpbmRleCIpCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLlBsYXF1ZVZ1bG5lcmFiaWxpdHlJbmRleC5ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKYGBgCgpgYGB7cn0KCmNvbXBhcmVfbWVhbnMoSERBQzkgfiBQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCnA1IDwtIGdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSwgCiAgICAgICAgICAgICAgICAgIHggPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiUGxhcXVlIHZ1bG5lcmFiaWxpdHkgaW5kZXgiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pXG4iLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgZmFjZXQuYnkgPSAiT1J5ZWFyR3JvdXAiLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLmZvcm1hdCIsICBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwYXIocDUsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiUGxhcXVlIHZ1bG5lcmFiaWxpdHkgaW5kZXgiKQpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIiwgVG9kYXksICIuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5QbGFxdWVWdWxuZXJhYmlsaXR5SW5kZXhfRmFjZXRfYnlZZWFyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCmBgYAoKYGBge3J9CmNvbXBhcmVfbWVhbnMoSERBQzkgfiBQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCwgZ3JvdXAuYnkgPSAiR2VuZGVyIiwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCnA2IDwtIGdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSwgCiAgICAgICAgICAgICAgICAgIHggPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiUGxhcXVlIHZ1bG5lcmFiaWxpdHkgaW5kZXgiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pXG4iLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiI0Q1MjY3QiIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGZhY2V0LmJ5ID0gIk9SeWVhckdyb3VwIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gR2VuZGVyKSwgbGFiZWwgPSAicC5mb3JtYXQiLCAgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncGFyKHA2LCBsZWdlbmQgPSAiYm90dG9tIiwgbGVnZW5kLnRpdGxlID0gIlBsYXF1ZSB2dWxuZXJhYmlsaXR5IGluZGV4IikKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsIFRvZGF5LCAiLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuUGxhcXVlVnVsbmVyYWJpbGl0eUluZGV4X0ZhY2V0X2J5WWVhci5ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKYGBgCgoKCiMjIyBNb2RlbCAxCgpJbiB0aGlzIG1vZGVsIHdlIGNvcnJlY3QgZm9yIF9BZ2VfLCBfR2VuZGVyXywgYW5kIF95ZWFyIG9mIHN1cmdlcnlfLgoKSGVyZSB3ZSB1c2UgdGhlIGludmVyc2UtcmFuayBub3JtYWxpemVkIGRhdGEgLSB2aXN1YWxseSB0aGlzIGlzIG1vcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuCgpBbmFseXNpcyBvZiB0aGUgcGxhcXVlIHZ1bG5lcmFiaWxpdHkgaW5kZXogYXMgYSBmdW5jdGlvbiBvZiBwbGFxdWUgdGFyZ2V0KHMpIGxldmVscy4KCmBgYHtyIENyb3NzU2VjOiBQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCAtIG9yZGluYWwgcmVncmVzc2lvbiBNT0RFTDEgUkFOSywgcGFnZWQucHJpbnQ9VFJVRX0KVFJBSVRTLlRBUkdFVC5SQU5LLmV4dHJhID0gYygiSERBQzkiKQoKR0xNLnJlc3VsdHMgPC0gZGF0YS5mcmFtZShtYXRyaXgoTkEsIG5jb2wgPSAxNiwgbnJvdyA9IDApKQpmb3IgKHByb3RlaW4gaW4gMTpsZW5ndGgoVFJBSVRTLlRBUkdFVC5SQU5LLmV4dHJhKSkgewogIFBST1RFSU4gPSBUUkFJVFMuVEFSR0VULlJBTksuZXh0cmFbcHJvdGVpbl0KICBjYXQocGFzdGUwKCJcbkFuYWx5c2lzIG9mICIsUFJPVEVJTiwiLlxuIikpCiAgVFJBSVQgPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiCiAgICBjYXQocGFzdGUwKCJcbi0gcHJvY2Vzc2luZyAiLFRSQUlULCJcblxuIikpCiAgICBjdXJyZW50REYgPC0gYXMuZGF0YS5mcmFtZShBRVJOQVNFLmNsaW4uaGRhYzkgJT4lCiAgICAgIGRwbHlyOjpzZWxlY3QoLiwgUFJPVEVJTiwgVFJBSVQsIENPVkFSSUFURVNfTTEpICU+JQogICAgICBmaWx0ZXIoY29tcGxldGUuY2FzZXMoLikpKSAlPiUKICAgICAgZmlsdGVyX2lmKH5pcy5udW1lcmljKC4pLCBhbGxfdmFycyghaXMuaW5maW5pdGUoLikpKSAlPiUKICAgICAgZHJvcGxldmVscyguKQogICAgCiAgICAjIGZpeCBudW1lcmljIE9SIHllYXIKICAgICMgY3VycmVudERGJE9SZGF0ZV95ZWFyIDwtIGFzLm51bWVyaWMoY3VycmVudERGJE9SZGF0ZV95ZWFyKQogICAgCiAgICAjIGZvciBkZWJ1ZwogICAgIyBwcmludChEVDo6ZGF0YXRhYmxlKGN1cnJlbnRERikpCiAgICAjIHByaW50KG5yb3coY3VycmVudERGKSkKICAgICMgcHJpbnQoc3RyKGN1cnJlbnRERikpCiAgICAjIHByaW50KGNsYXNzKGN1cnJlbnRERlssVFJBSVRdKSkKICAgICMgdGFibGUoY3VycmVudERGJE9SZGF0ZV95ZWFyKQogICAgIyMjIHVuaXZhcmlhdGUKICAgICAjICsgSHlwZXJ0ZW5zaW9uLmNvbXBvc2l0ZSArIERpYWJldGVzU3RhdHVzICsgU21va2VyQ3VycmVudCArIAogICAgICMgICAgICAgICAgICBNZWQuU3RhdGluLkxMRCArIE1lZC5hbGwuYW50aXBsYXRlbGV0ICsgR0ZSX01EUkQgKyBCTUkgKyAKICAgICAjICAgICAgICAgICAgQ0FEX2hpc3RvcnkgKyBTdHJva2VfaGlzdG9yeSArIFBlcmlwaGVyYWwuaW50ZXJ2ICsgc3Rlbm9zZQogICAgIyBmaXQgPC0gcG9scihjdXJyZW50REZbLFRSQUlUXSB+IGN1cnJlbnRERlssUFJPVEVJTl0gKyBBZ2UgKyBHZW5kZXIgKyBPUmRhdGVfeWVhciwgCiAgICAjICAgICAgICAgICBkYXRhICA9ICBjdXJyZW50REYsIAogICAgIyAgICAgICAgICAgSGVzcyA9IFRSVUUpCiAgICBmaXQgPC0gcG9scihjdXJyZW50REZbLFRSQUlUXSB+IGN1cnJlbnRERlssUFJPVEVJTl0gKyBBZ2UgKyBHZW5kZXIgKyBPUmRhdGVfZXBvY2gsIAogICAgICAgICAgICAgIGRhdGEgID0gIGN1cnJlbnRERiwgCiAgICAgICAgICAgICAgSGVzcyA9IFRSVUUpCiAgICBwcmludChzdW1tYXJ5KGZpdCkpCiAgICAKICAgICMjIHN0b3JlIHRhYmxlCiAgICAoY3RhYmxlIDwtIGNvZWYoc3VtbWFyeShmaXQpKSkKCiAgICAjIyBjYWxjdWxhdGUgYW5kIHN0b3JlIHAgdmFsdWVzCiAgICBwIDwtIHBub3JtKGFicyhjdGFibGVbLCAidCB2YWx1ZSJdKSwgbG93ZXIudGFpbCA9IEZBTFNFKSAqIDIKICAgIAogICAgIyMgY29tYmluZWQgdGFibGUKICAgIHByaW50KChjdGFibGUgPC0gY2JpbmQoY3RhYmxlLCAicCB2YWx1ZSIgPSBwKSkpCiAgfQoKCmBgYAoKIyMjIE1vZGVsIDIKCkluIHRoaXMgbW9kZWwgd2UgY29ycmVjdCBmb3IgX0FnZV8sIF9HZW5kZXJfLCBfSHlwZXJ0ZW5zaW9uIHN0YXR1c18sIF9EaWFiZXRlcyBzdGF0dXNfLCBfY3VycmVudCBzbW9rZXIgc3RhdHVzXywgX2xpcGlkLWxvd2VyaW5nIGRydWdzIChMTERzKV8sIF9hbnRpcGxhdGVsZXQgbWVkaWNhdGlvbl8sIF9lR0ZSIChNRFJEKV8sIF9CTUlfLCBfTWVkSHhfQ1ZEXyAoY29tYmluYXRpb24gb2YgX0NBRCBoaXN0b3J5XywgX3N0cm9rZSBoaXN0b3J5XywgYW5kIF9wZXJpcGhlcmFsIGludGVydmVudGlvbnNfKSwgYW5kIF9zdGVub3Npcy5fLgoKCmBgYHtyIENyb3NzU2VjOiBQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCAtIG9yZGluYWwgcmVncmVzc2lvbiBNT0RFTDIgUkFOSywgcGFnZWQucHJpbnQ9VFJVRX0KCmZvciAocHJvdGVpbiBpbiAxOmxlbmd0aChUUkFJVFMuVEFSR0VULlJBTksuZXh0cmEpKSB7CiAgUFJPVEVJTiA9IFRSQUlUUy5UQVJHRVQuUkFOSy5leHRyYVtwcm90ZWluXQogIGNhdChwYXN0ZTAoIlxuQW5hbHlzaXMgb2YgIixQUk9URUlOLCIuXG4iKSkKICBUUkFJVCA9ICJQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCIKICAgIGNhdChwYXN0ZTAoIlxuLSBwcm9jZXNzaW5nICIsVFJBSVQsIlxuXG4iKSkKICAgIGN1cnJlbnRERiA8LSBhcy5kYXRhLmZyYW1lKEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUKICAgICAgZHBseXI6OnNlbGVjdCguLCBQUk9URUlOLCBUUkFJVCwgQ09WQVJJQVRFU19NMikgJT4lCiAgICAgIGZpbHRlcihjb21wbGV0ZS5jYXNlcyguKSkpICU+JQogICAgICBmaWx0ZXJfaWYofmlzLm51bWVyaWMoLiksIGFsbF92YXJzKCFpcy5pbmZpbml0ZSguKSkpICU+JQogICAgICBkcm9wbGV2ZWxzKC4pCiAgICAKICAgICMgZml4IG51bWVyaWMgT1IgeWVhcgogICAgIyBjdXJyZW50REYkT1JkYXRlX3llYXIgPC0gYXMubnVtZXJpYyhjdXJyZW50REYkT1JkYXRlX3llYXIpCiAgICAKICAgICMgZm9yIGRlYnVnCiAgICAjIHByaW50KERUOjpkYXRhdGFibGUoY3VycmVudERGKSkKICAgICMgcHJpbnQobnJvdyhjdXJyZW50REYpKQogICAgIyBwcmludChzdHIoY3VycmVudERGKSkKICAgICMgcHJpbnQoY2xhc3MoY3VycmVudERGWyxUUkFJVF0pKQogICAgIyMjIHVuaXZhcmlhdGUKCiAgICAjIGZpdCA8LSBwb2xyKGFzLmZhY3RvcihjdXJyZW50REZbLFRSQUlUXSkgfiBjdXJyZW50REZbLFBST1RFSU5dICsgQWdlICsgR2VuZGVyICsgT1JkYXRlX3llYXIgKyBIeXBlcnRlbnNpb24uY29tcG9zaXRlICsgRGlhYmV0ZXNTdGF0dXMgKyBTbW9rZXJTdGF0dXMgKyBNZWQuU3RhdGluLkxMRCArIE1lZC5hbGwuYW50aXBsYXRlbGV0ICsgR0ZSX01EUkQgKyBCTUkgKyBNZWRIeF9DVkQgKyBzdGVub3NlLAogICAgIyAgICAgICAgICAgZGF0YSAgPSAgY3VycmVudERGLAogICAgIyAgICAgICAgICAgSGVzcyA9IFRSVUUpCiAgICAKICAgIGZpdCA8LSBwb2xyKGFzLmZhY3RvcihjdXJyZW50REZbLFRSQUlUXSkgfiBjdXJyZW50REZbLFBST1RFSU5dICsgQWdlICsgR2VuZGVyICsgT1JkYXRlX2Vwb2NoICsgSHlwZXJ0ZW5zaW9uLmNvbXBvc2l0ZSArIERpYWJldGVzU3RhdHVzICsgU21va2VyU3RhdHVzICsgTWVkLlN0YXRpbi5MTEQgKyBNZWQuYWxsLmFudGlwbGF0ZWxldCArIEdGUl9NRFJEICsgQk1JICsgTWVkSHhfQ1ZEICsgc3Rlbm9zZSwKICAgICAgICAgICAgICBkYXRhICA9ICBjdXJyZW50REYsCiAgICAgICAgICAgICAgSGVzcyA9IFRSVUUpCiAgICAKICAgIHByaW50KHN1bW1hcnkoZml0KSkKICAgIAogICAgIyMgc3RvcmUgdGFibGUKICAgIChjdGFibGUgPC0gY29lZihzdW1tYXJ5KGZpdCkpKQoKICAgICMjIGNhbGN1bGF0ZSBhbmQgc3RvcmUgcCB2YWx1ZXMKICAgIHAgPC0gcG5vcm0oYWJzKGN0YWJsZVssICJ0IHZhbHVlIl0pLCBsb3dlci50YWlsID0gRkFMU0UpICogMgogICAgCiAgICAjIyBjb21iaW5lZCB0YWJsZQogICAgcHJpbnQoKGN0YWJsZSA8LSBjYmluZChjdGFibGUsICJwIHZhbHVlIiA9IHApKSkKICB9CgpgYGAKCiMjIyBTYXZpbmcgZGF0YSBmb3Igc2hhcmUKCldlIGFsc28gd2FudCB0byBzaGFyZSB0aGUgZGF0YSB3aXRoIG91ciBjb2xsYWJvcmF0b3JzLiBBbmQgcHJvdmlkZSBzb21lIG1vcmUgZ3JhcGhzIGFuZCBzdW1tYXJ5IHN0YXRpc3RpY3MgdG9vLgoKYGBge3J9CnN1bW1hcnkoQUVSTkFTRS5jbGluLmhkYWM5JEhEQUM5KQpgYGAKCgpgYGB7cn0KZ2dwdWJyOjpnZ2hpc3RvZ3JhbShBRVJOQVNFLmNsaW4uaGRhYzksIHggPSAiSERBQzkiLAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsIGZpbGwgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgICBhZGQgPSAibWVhbiIsIGFkZF9kZW5zaXR5ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICB4bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIpCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkhpc3RvZ3JhbS5ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKZ2dwdWJyOjpnZ2hpc3RvZ3JhbShBRVJOQVNFLmNsaW4uaGRhYzksIHggPSAiSERBQzkiLAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIlBsYXF1ZV9WdWxuZXJhYmlsaXR5X0luZGV4IiwgZmlsbCA9ICJQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCIsCiAgICAgICAgICAgICAgICAgICAgZmFjZXQuYnkgPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiLAogICAgICAgICAgICAgICAgICAgIGFkZCA9ICJtZWFuIiwgYWRkX2RlbnNpdHkgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgIHhsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikiLAogICAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIikKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsIFRvZGF5LCAiLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuSGlzdG9ncmFtLkZhY2V0YnlQVkkucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmdncHVicjo6Z2doaXN0b2dyYW0oQUVSTkFTRS5jbGluLmhkYWM5LCB4ID0gIkhEQUM5IiwKICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCIsIGZpbGwgPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiLAogICAgICAgICAgICAgICAgICAgICMgZmFjZXQuYnkgPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiLAogICAgICAgICAgICAgICAgICAgIGFkZCA9ICJtZWFuIiwgYWRkX2RlbnNpdHkgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgIHhsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikiLAogICAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIikKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsIFRvZGF5LCAiLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuSGlzdG9ncmFtLmJ5UFZJLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgoKZ2dwdWJyOjpnZ2hpc3RvZ3JhbShBRVJOQVNFLmNsaW4uaGRhYzksIHggPSAiSERBQzkiLAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSAiYmxhY2siLCBydWcgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgIGFkZCA9ICJtZWFuIiwgYWRkX2RlbnNpdHkgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgIHhsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikiLAogICAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIikKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsIFRvZGF5LCAiLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuSGlzdG9ncmFtLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpgYGAKCmBgYHtyfQpBRVJOQVNFLmNsaW4uaGRhYzkuZm9yU0hBUkUgPC0gc3Vic2V0KEFFUk5BU0UuY2xpbi5oZGFjOSwgc2VsZWN0ID0gYygiU1RVRFlfTlVNQkVSIiwgIkFnZSIsICJHZW5kZXIiLCAiSERBQzkiLCAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiKSkKYGBgCgoKYGBge3J9CnNhdmVSRFMoQUVSTkFTRS5jbGluLmhkYWM5LmZvclNIQVJFLCBmaWxlID0gcGFzdGUwKE9VVF9sb2MsICIvIiwgVG9kYXksICIuIixUUkFJVF9PRl9JTlRFUkVTVCwiLkFFUk5BU0UuY2xpbi5oZGFjOS5mb3JTSEFSRS5yZHMiKSkKCmZ3cml0ZShBRVJOQVNFLmNsaW4uaGRhYzkuZm9yU0hBUkUsIGZpbGUgPSBwYXN0ZTAoT1VUX2xvYywgIi8iLCBUb2RheSwgIi4iLFRSQUlUX09GX0lOVEVSRVNULCIuQUVSTkFTRS5jbGluLmhkYWM5LmZvclNIQVJFLnR4dCIpLAogICAgICAgc2VwID0gIlx0IiwKICAgICAgIHF1b3RlID0gRkFMU0UsCiAgICAgICBuYSA9ICJOQSIsIAogICAgICAgdmVyYm9zZSA9IFRSVUUsIHNob3dQcm9ncmVzcyA9IFRSVUUsIG5UaHJlYWQgPSA4KQpgYGAKCgojIyBQbG90dGluZyBIREFDOSB2cyBGYXQgMTAgcGVyYy4gaW4gdGhlIHBsYXF1ZQoKYGBge3J9CiMgR2xvYmFsIHRlc3QKY29tcGFyZV9tZWFucyhIREFDOSB+IEZhdC5iaW5fMTAsICBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKEZhdC5iaW5fMTApKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCnAxIDwtIGdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShGYXQuYmluXzEwKSksIAogICAgICAgICAgICAgICAgICB4ID0gIkZhdC5iaW5fMTAiLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiRmF0IDwxMCUgdnMgPjEwJSIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbilcbiIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkZhdC5iaW5fMTAiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGxhYmVsID0gInAuZm9ybWF0IiwgIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3BhcihwMSwgbGVnZW5kID0gImJvdHRvbSIsIGxlZ2VuZC50aXRsZSA9ICJGYXQgPDEwJSB2cyA+MTAlIikKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsIFRvZGF5LCAiLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5wbGFxdWUuRmF0LmJpbl8xMC5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQpgYGAKCmBgYHtyfQpjb21wYXJlX21lYW5zKEhEQUM5IH4gRmF0LmJpbl8xMCwgZ3JvdXAuYnkgPSAiR2VuZGVyIiwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShGYXQuYmluXzEwKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpwMiA8LSBnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoRmF0LmJpbl8xMCkpLCAKICAgICAgICAgICAgICAgICAgeCA9ICJGYXQuYmluXzEwIiwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkZhdCA8MTAlIHZzID4xMCUgYnkgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKVxuIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsICBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwYXIocDIsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiRmF0IDwxMCUgdnMgPjEwJSBieSBnZW5kZXIiKQpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIiwgVG9kYXksICIuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5GYXQuYmluXzEwLmJ5R2VuZGVyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpgYGAKCmBgYHtyfQpjb21wYXJlX21lYW5zKEhEQUM5IH4gRmF0LmJpbl8xMCwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShGYXQuYmluXzEwKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpwNSA8LSBnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoRmF0LmJpbl8xMCkpLCAKICAgICAgICAgICAgICAgICAgeCA9ICJGYXQuYmluXzEwIiwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkZhdCA8MTAlIHZzID4xMCUgYnkgeWVhciBvZiBzdXJnZXJ5IiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKVxuIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiRmF0LmJpbl8xMCIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgZmFjZXQuYnkgPSAiT1J5ZWFyR3JvdXAiLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLmZvcm1hdCIsICBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwYXIocDUsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiRmF0IDwxMCUgdnMgPjEwJSBieSB5ZWFyIG9mIHN1cmdlcnkiKQpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIiwgVG9kYXksICIuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5GYXQuYmluXzEwX0ZhY2V0X2J5WWVhci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQpgYGAKCmBgYHtyfQpjb21wYXJlX21lYW5zKEhEQUM5IH4gRmF0LmJpbl8xMCwgZ3JvdXAuYnkgPSAiR2VuZGVyIiwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShGYXQuYmluXzEwKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpwNiA8LSBnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoRmF0LmJpbl8xMCkpLCAKICAgICAgICAgICAgICAgICAgeCA9ICJGYXQuYmluXzEwIiwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkZhdCA8MTAlIHZzID4xMCUgYnkgeWVhciBvZiBzdXJnZXJ5IGFuZCBnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pXG4iLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiI0Q1MjY3QiIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGZhY2V0LmJ5ID0gIk9SeWVhckdyb3VwIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gR2VuZGVyKSwgbGFiZWwgPSAicC5mb3JtYXQiLCAgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncGFyKHA2LCBsZWdlbmQgPSAiYm90dG9tIiwgbGVnZW5kLnRpdGxlID0gIkZhdCA8MTAlIHZzID4xMCUgYnkgeWVhciBvZiBzdXJnZXJ5IGFuZCBnZW5kZXIiKQpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIiwgVG9kYXksICIuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5GYXQuYmluXzEwX0ZhY2V0X2J5WWVhci5ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQpgYGAKCiMjIFBsb3R0aW5nIEhEQUM5IHZzIEZhdCA0MCBwZXJjLiBpbiB0aGUgcGxhcXVlCgpgYGB7cn0KIyBHbG9iYWwgdGVzdApjb21wYXJlX21lYW5zKEhEQUM5IH4gRmF0LmJpbl80MCwgIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoRmF0LmJpbl80MCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDEgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKEZhdC5iaW5fNDApKSwgCiAgICAgICAgICAgICAgICAgIHggPSAiRmF0LmJpbl80MCIsCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJGYXQgPDQwJSB2cyA+NDAlIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKVxuIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiRmF0LmJpbl80MCIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwgPSAicC5mb3JtYXQiLCAgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncGFyKHAxLCBsZWdlbmQgPSAiYm90dG9tIiwgbGVnZW5kLnRpdGxlID0gIkZhdCA8NDAlIHZzID40MCUiKQpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIiwgVG9kYXksICIuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5GYXQuYmluXzQwLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCmBgYAoKYGBge3J9CmNvbXBhcmVfbWVhbnMoSERBQzkgfiBGYXQuYmluXzQwLCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKEZhdC5iaW5fNDApKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCnAyIDwtIGdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShGYXQuYmluXzQwKSksIAogICAgICAgICAgICAgICAgICB4ID0gIkZhdC5iaW5fNDAiLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiRmF0IDw0MCUgdnMgPjQwJSBieSBnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pXG4iLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiI0Q1MjY3QiIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IEdlbmRlciksIGxhYmVsID0gInAuZm9ybWF0IiwgIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3BhcihwMiwgbGVnZW5kID0gImJvdHRvbSIsIGxlZ2VuZC50aXRsZSA9ICJGYXQgPDQwJSB2cyA+NDAlIGJ5IGdlbmRlciIpCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkZhdC5iaW5fNDAuYnlHZW5kZXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmBgYAoKYGBge3J9CmNvbXBhcmVfbWVhbnMoSERBQzkgfiBGYXQuYmluXzQwLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKEZhdC5iaW5fNDApKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCnA1IDwtIGdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShGYXQuYmluXzQwKSksIAogICAgICAgICAgICAgICAgICB4ID0gIkZhdC5iaW5fNDAiLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiRmF0IDw0MCUgdnMgPjQwJSBieSB5ZWFyIG9mIHN1cmdlcnkiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pXG4iLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJGYXQuYmluXzQwIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICBmYWNldC5ieSA9ICJPUnllYXJHcm91cCIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGxhYmVsID0gInAuZm9ybWF0IiwgIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3BhcihwNSwgbGVnZW5kID0gImJvdHRvbSIsIGxlZ2VuZC50aXRsZSA9ICJGYXQgPDQwJSB2cyA+NDAlIGJ5IHllYXIgb2Ygc3VyZ2VyeSIpCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkZhdC5iaW5fNDBfRmFjZXRfYnlZZWFyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCmBgYAoKYGBge3J9CmNvbXBhcmVfbWVhbnMoSERBQzkgfiBGYXQuYmluXzQwLCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKEZhdC5iaW5fNDApKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCnA2IDwtIGdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShGYXQuYmluXzQwKSksIAogICAgICAgICAgICAgICAgICB4ID0gIkZhdC5iaW5fNDAiLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiRmF0IDw0MCUgdnMgPjQwJSBieSB5ZWFyIG9mIHN1cmdlcnkgYW5kIGdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbilcbiIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgZmFjZXQuYnkgPSAiT1J5ZWFyR3JvdXAiLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsICBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwYXIocDYsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiRmF0IDw0MCUgdnMgPjQwJSBieSB5ZWFyIG9mIHN1cmdlcnkgYW5kIGdlbmRlciIpCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkZhdC5iaW5fNDBfRmFjZXRfYnlZZWFyLmJ5R2VuZGVyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCmBgYAoKCiMjIFBsb3R0aW5nIEhEQUM5IHZzIElQSCBpbiB0aGUgcGxhcXVlCgpgYGB7cn0KIyBHbG9iYWwgdGVzdApjb21wYXJlX21lYW5zKEhEQUM5IH4gSVBILmJpbiwgIGRhdGEgPSBBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoSVBILmJpbikpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDEgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKElQSC5iaW4pKSwgCiAgICAgICAgICAgICAgICAgIHggPSAiSVBILmJpbiIsCiAgICAgICAgICAgICAgICAgIHkgPSAiSERBQzkiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJJbnRyYXBsYXF1ZSBoZW1vcnJoYWdlIChubyB2cy4geWVzKSIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbilcbiIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIklQSC5iaW4iLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGxhYmVsID0gInAuZm9ybWF0IiwgIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3BhcihwMSwgbGVnZW5kID0gImJvdHRvbSIsIGxlZ2VuZC50aXRsZSA9ICJJUEgiKQpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIiwgVG9kYXksICIuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5JUEguYmluLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCmBgYAoKYGBge3J9CmNvbXBhcmVfbWVhbnMoSERBQzkgfiBJUEguYmluLCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKElQSC5iaW4pKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCnAyIDwtIGdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShJUEguYmluKSksIAogICAgICAgICAgICAgICAgICB4ID0gIklQSC5iaW4iLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiSW50cmFwbGFxdWUgaGVtb3JyaGFnZSAobm8gdnMuIHllcykgYnkgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKVxuIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsICBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwYXIocDIsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiSVBIIGJ5IGdlbmRlciIpCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLklQSC5iaW4uYnlHZW5kZXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmBgYAoKYGBge3J9CmNvbXBhcmVfbWVhbnMoSERBQzkgfiBJUEguYmluLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKElQSC5iaW4pKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCnA1IDwtIGdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShJUEguYmluKSksIAogICAgICAgICAgICAgICAgICB4ID0gIklQSC5iaW4iLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiSW50cmFwbGFxdWUgaGVtb3JyaGFnZSAobm8gdnMuIHllcykgYnkgeWVhciBvZiBzdXJnZXJ5IiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKVxuIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiSVBILmJpbiIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgZmFjZXQuYnkgPSAiT1J5ZWFyR3JvdXAiLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLmZvcm1hdCIsICBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwYXIocDUsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiSVBIIGJ5IHllYXIgb2Ygc3VyZ2VyeSIpCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLklQSC5iaW5fRmFjZXRfYnlZZWFyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCmBgYAoKYGBge3J9CmNvbXBhcmVfbWVhbnMoSERBQzkgfiBJUEguYmluLCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKElQSC5iaW4pKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCnA2IDwtIGdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShJUEguYmluKSksIAogICAgICAgICAgICAgICAgICB4ID0gIklQSC5iaW4iLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiSW50cmFwbGFxdWUgaGVtb3JyaGFnZSAobm8gdnMuIHllcykgYnkgeWVhciBvZiBzdXJnZXJ5IGFuZCBnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pXG4iLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiI0Q1MjY3QiIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGZhY2V0LmJ5ID0gIk9SeWVhckdyb3VwIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gR2VuZGVyKSwgbGFiZWwgPSAicC5mb3JtYXQiLCAgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncGFyKHA2LCBsZWdlbmQgPSAiYm90dG9tIiwgbGVnZW5kLnRpdGxlID0gIklQSCBieSB5ZWFyIG9mIHN1cmdlcnkgYW5kIGdlbmRlciIpCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLklQSC5iaW5fRmFjZXRfYnlZZWFyLmJ5R2VuZGVyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCmBgYAoKIyMgUGxvdHRpbmcgSERBQzkgdnMgQ2FsY2lmaWNhdGlvbiBpbiB0aGUgcGxhcXVlCgpgYGB7cn0KIyBHbG9iYWwgdGVzdApjb21wYXJlX21lYW5zKEhEQUM5IH4gQ2FsYy5iaW4sICBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKENhbGMuYmluKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpwMSA8LSBnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoQ2FsYy5iaW4pKSwgCiAgICAgICAgICAgICAgICAgIHggPSAiQ2FsYy5iaW4iLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiQ2FsY2lmaWNhdGlvbiAobm8vbWlub3IgdnMuIG1vZGVyYXRlL2hlYXZ5KSIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbilcbiIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkNhbGMuYmluIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLmZvcm1hdCIsICBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwYXIocDEsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiQ2FsY2lmaWNhdGlvbiIpCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkNhbGMuYmluLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCmBgYAoKYGBge3J9CmNvbXBhcmVfbWVhbnMoSERBQzkgfiBDYWxjLmJpbiwgZ3JvdXAuYnkgPSAiR2VuZGVyIiwgZGF0YSA9IEFFUk5BU0UuY2xpbi5oZGFjOSAlPiUgZmlsdGVyKCFpcy5uYShDYWxjLmJpbikpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDIgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKENhbGMuYmluKSksIAogICAgICAgICAgICAgICAgICB4ID0gIkNhbGMuYmluIiwKICAgICAgICAgICAgICAgICAgeSA9ICJIREFDOSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkNhbGNpZmljYXRpb24gKG5vL21pbm9yIHZzLiBtb2RlcmF0ZS9oZWF2eSkgYnkgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJIREFDOSAobm9ybWFsaXplZCBleHByZXNzaW9uKVxuIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsICBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwYXIocDIsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiQ2FsY2lmaWNhdGlvbiBieSBnZW5kZXIiKQpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIiwgVG9kYXksICIuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5DYWxjLmJpbi5ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKYGBgCgpgYGB7cn0KY29tcGFyZV9tZWFucyhIREFDOSB+IENhbGMuYmluLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKENhbGMuYmluKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpwNSA8LSBnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoQ2FsYy5iaW4pKSwgCiAgICAgICAgICAgICAgICAgIHggPSAiQ2FsYy5iaW4iLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiQ2FsY2lmaWNhdGlvbiAobm8vbWlub3IgdnMuIG1vZGVyYXRlL2hlYXZ5KSBieSB5ZWFyIG9mIHN1cmdlcnkiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhEQUM5IChub3JtYWxpemVkIGV4cHJlc3Npb24pXG4iLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJDYWxjLmJpbiIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgZmFjZXQuYnkgPSAiT1J5ZWFyR3JvdXAiLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLmZvcm1hdCIsICBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwYXIocDUsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiQ2FsY2lmaWNhdGlvbiBieSB5ZWFyIG9mIHN1cmdlcnkiKQpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIiwgVG9kYXksICIuIixUUkFJVF9PRl9JTlRFUkVTVCwiLnBsYXF1ZS5DYWxjLmJpbl9GYWNldF9ieVllYXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKYGBgCgpgYGB7cn0KY29tcGFyZV9tZWFucyhIREFDOSB+IENhbGMuYmluLCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluLmhkYWM5ICU+JSBmaWx0ZXIoIWlzLm5hKENhbGMuYmluKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpwNiA8LSBnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4uaGRhYzkgJT4lIGZpbHRlcighaXMubmEoQ2FsYy5iaW4pKSwgCiAgICAgICAgICAgICAgICAgIHggPSAiQ2FsYy5iaW4iLAogICAgICAgICAgICAgICAgICB5ID0gIkhEQUM5IiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiQ2FsY2lmaWNhdGlvbiAobm8vbWlub3IgdnMuIG1vZGVyYXRlL2hlYXZ5KSBieSB5ZWFyIG9mIHN1cmdlcnkgYW5kIGdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSERBQzkgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbilcbiIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgZmFjZXQuYnkgPSAiT1J5ZWFyR3JvdXAiLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsICBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwYXIocDYsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiQ2FsY2lmaWNhdGlvbiBieSB5ZWFyIG9mIHN1cmdlcnkgYW5kIGdlbmRlciIpCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi4iLFRSQUlUX09GX0lOVEVSRVNULCIucGxhcXVlLkNhbGMuYmluX0ZhY2V0X2J5WWVhci5ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQpgYGAKCgoKIyBTZXNzaW9uIGluZm9ybWF0aW9uCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKICAgIFZlcnNpb246ICAgICAgdjEuMC41CiAgICBMYXN0IHVwZGF0ZTogIDIwMjMtMDUtMzEKICAgIFdyaXR0ZW4gYnk6ICAgU2FuZGVyIFcuIHZhbiBkZXIgTGFhbiAocy53LnZhbmRlcmxhYW4tMlthdF11bWN1dHJlY2h0Lm5sKS4KICAgIERlc2NyaXB0aW9uOiAgU2NyaXB0IHRvIGFuYWx5c2UgSERBQzkgZnJvbSB0aGUgQXRoZXItRXhwcmVzcyBCaW9iYW5rIFN0dWR5LgogICAgTWluaW11bSByZXF1aXJlbWVudHM6IFIgdmVyc2lvbiAzLjUuMiAoMjAxOC0xMi0yMCkgLS0gJ0VnZ3NoZWxsIElnbG9vJywgbWFjT1MgTW9qYXZlICgxMC4xNC4yKS4KICAgIAogICAgKipNb1NDb1cgVG8tRG8gTGlzdCoqCiAgICBUaGUgdGhpbmdzIHdlIE11c3QsIFNob3VsZCwgQ291bGQsIGFuZCBXb3VsZCBoYXZlIGdpdmVuIHRoZSB0aW1lIHdlIGhhdmUuCiAgICBfTV8KCiAgICBfU18KCiAgICBfQ18KCiAgICBfV18KCiAgICAqKkNoYW5nZXMgbG9nKioKICAgICogdjEuMC41IEZpeGVkIGZvcmVzdCBwbG90IGFuZCBhbHRlcm5hdGl2ZSBib3hwbG90IGZvciBzeW1wdG9tcy4KICAgICogdjEuMC40IE1hZGUgaGlzdG9ncmFtIG9mIFBWSS4gRXhwb3J0ZWQgSERBQzkgYW5kIFBWSSBkYXRhLgogICAgKiB2MS4wLjMgU21hbGwgYWRhcHRhdGlvbnMgdG8gUFZJLXBsb3RzLgogICAgKiB2MS4wLjIgQ2hhbmdlZCB0aGUgUFZJLXBsb3QuCiAgICAqIHYxLjAuMSBBZGRlZCBmaWd1cmVzIG9uIGZhdCBpbiB0aGUgcGxhcXVlLgogICAgKiB2MS4wLjAgSW5pdGFsIHZlcnNpb24uCiAgICAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpgYGB7ciBldmFsID0gVFJVRX0Kc2Vzc2lvbkluZm8oKQpgYGAKCiMgU2F2aW5nIGVudmlyb25tZW50CmBgYHtyIFNhdmluZ30Kc2F2ZS5pbWFnZShwYXN0ZTAoUFJPSkVDVF9sb2MsICIvIixUb2RheSwiLiIsUFJPSkVDVE5BTUUsIi5idWxrUk5Bc2VxLmFkZGl0aW9uYWxfZmlndXJlcy5SRGF0YSIpKQpgYGAKCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCA8c3VwPsKpIDE5NzktMjAyMyBTYW5kZXIgVy4gdmFuIGRlciBMYWFuIHwgcy53LnZhbmRlcmxhYW5bYXRdZ21haWwuY29tIHwgW3ZhbmRlcmxhYW4uc2NpZW5jZV0oaHR0cHM6Ly92YW5kZXJsYWFuLnNjaWVuY2UpLjwvc3VwPiB8CistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKCgo=